Luogu P4479 [BJWC2018]第k大斜率
一道清真简单的好写的题
题意
求点集两两连出的直线中斜率第$ 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('\n');} 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; }