Luogu P4479 [BJWC2018]第k大斜率

一道清真简单的好写的题

Luogu P4479


题意

求点集两两连出的直线中斜率第$ k$大的直线


$ Solution$

二分答案,设$x_j geq x_i$

若点$ (x_i,y_i)$和点$(x_j,y_j)$构成的斜率大于二分的答案$ k$则有

$ frac{y_j-y_i}{x_j-x_i} geq k$

$y_j-k·x_j geq y_i-k·x_i$

转化成二维偏序

树状数组/归并排序维护即可

注意特判各种边界问题

时间复杂度$ O(n log^2 n)$


$ my code$

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x = 0; char zf = 1; char ch = getchar();
    while (ch != '-' && !isdigit(ch)) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('
');}
ll n,m;
struct node{
    int x,y;
    bool operator <(const node s)const{
        if(x==s.x)return y>s.y;
        return x<s.x;
    }
}a[100010];
ll q[100010],zs[100010],ans;
ll calc(int L,int R){
    if(L==R)return 0;
    if(ans>=m)return ans;
    const int mid=L+R>>1;
    calc(L,mid);calc(mid+1,R);if(ans>=m)return ans;
    for(rt i=mid+1,j=L;i<=R;i++){
        while(j<=mid&&q[j]<=q[i])j++;
        ans+=j-L;
    }
    int tot1=L,tot2=mid+1,pl=L;
    while(tot1<=mid||tot2<=R){
        if(tot1>mid||(q[tot1]>q[tot2]&&tot2<=R))zs[pl++]=q[tot2++];
        else zs[pl++]=q[tot1++];
    }
    for(rt i=L;i<=R;i++)q[i]=zs[i];
    return ans;
}
bool check(int x){
    ans=0;
    for(rt i=1;i<=n;i++)q[i]=(ll)a[i].y-(ll)x*a[i].x;
    return (calc(1,n)>=m);
}
int main(){
    n=read();m=read();
    for(rt i=1;i<=n;i++)a[i].x=read(),a[i].y=read();
    sort(a+1,a+n+1);
    int L=-200000000,R=200000000;
    while(L<=R){
        const int mid=L+R>>1;
        if(check(mid))L=mid+1;
        else R=mid-1;
    }
    write(R);
    return 0;
}
原文地址:https://www.cnblogs.com/DreamlessDreams/p/10083396.html