洛谷P4479第K大斜率
P4479第K大斜率
我采用了二分答案和树状数组统计顺序对的方法解决第k大斜率问题。以下是对代码思路的总结和关键点分析:
代码思路总结:
-
输入与预处理:
- 读入
n
个点的坐标,存储到数组arr
中。 - 对点按
x
坐标升序排序,若x
相同则按y
降序排序(避免斜率不存在的点对影响统计)。
- 读入
-
二分答案框架:
- 二分斜率
mid
,初始范围[-1e9, 1e9]
。 - 对于每个
mid
,通过check(mid)
函数判断是否存在至少k
条直线的斜率 ≥mid
。
- 二分斜率
-
check(mid)
函数:- 计算转换值:对每个点计算转换值
z_i = y_i - mid * x_i
。 - 离散化:将
z_i
排序并离散化为排名(相同值赋予相同排名)。 - 树状数组统计顺序对:
- 按原顺序(排序后的点顺序)遍历每个点。
- 查询树状数组中排名 ≤ 当前点排名的点的个数(即满足
z_j ≥ z_i
且j < i
的点对数)。 - 累加顺序对数量,若累计值 ≥
k
则返回true
。 - 更新树状数组(当前点排名位置 +1)。
- 若遍历结束累计值 <
k
,返回false
。
- 计算转换值:对每个点计算转换值
-
二分调整:
- 若
check(mid)
为true
,说明mid
偏小(有足够多斜率 ≥mid
的直线),调整左边界l = mid
。 - 若为
false
,说明mid
偏大,调整右边界r = mid
。 - 最终
l
即为第k
大斜率的向下取整结果。
- 若
-
输出:二分结束后输出
l
。
关键点分析:
- 避免斜率不存在:通过排序策略(
x
相同时y
降序)确保x
相同的点不会产生有效顺序对(因z_i
严格递减),从而排除斜率不存在的点对。 - 离散化优化:将
z_i
离散化为排名,将值域压缩到[1, n]
,适合树状数组处理。 - 顺序对统计:树状数组动态维护已遍历点的排名分布,高效查询当前点的顺序对数量。
- 二分正确性:二分目标为最大的
mid
满足至少k
条直线斜率 ≥mid
,该mid
即第k
大斜率的向下取整值。 - 复杂度:二分过程
O(log(2e9))
,每次check
离散化O(n log n)
,树状数组操作O(n log n)
,总复杂度O(n log n log(2e9))
,可接受。
代码优化建议:
- 提前退出优化:在
check
函数中,一旦累计顺序对数量 ≥k
即可提前返回true
,减少无效计算。 - 树状数组清零:每次
check
前通过初始化保证树状数组归零,避免脏数据。 - 离散化处理:对相同
z_i
赋予相同排名,确保顺序对统计正确性。
代码处理了边界情况(如大值域、斜率不存在、重复 z_i
等),能高效解决大规模数据问题。最终输出向下取整结果符合题目要求。
AC代码
#include<bits/stdc++.h>
#define ll long long
#define samplepassediscorrect for(;;)
#define ull unsigned long long
#define jiasu ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define lowbit(x) (x&(-x))
#define N 100050
using namespace std;
struct point{ll x,y;
}arr[N];
bool cmp(point &s1,point &s2){if(s1.x==s2.x) return s1.y>s2.y;return s1.x<s2.x;
}
ll n,b[N],k;
struct Fenwick_Tree{ll tree[N]={0};void update(ll x,ll d){while(x<=n){tree[x]+=d;x+=lowbit(x);}}ll sum(ll x){ll ans=0;while(x>0){ans+=tree[x];x-=lowbit(x);}return ans;}
};
struct temp{ll x,num;
}a[N];
bool cmp2(temp &s1,temp &s2){return s1.x<s2.x;}
bool check(ll mid){for(int i=1;i<=n;i++){a[i].num=i;a[i].x=arr[i].y-mid*arr[i].x;}sort(a+1,a+1+n,cmp2);b[a[1].num]=1;ll now=a[1].x,cnt=1;for(int i=2;i<=n;i++){if(a[i].x!=now){b[a[i].num]=++cnt;now=a[i].x;}else b[a[i].num]=cnt;}Fenwick_Tree r;ll sum=0;for(int i=1;i<=n;i++){ sum+=r.sum(b[i]);r.update(b[i],1);if(sum>=k) return true;} return false;
}
int main(){
jiasu;
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>arr[i].x>>arr[i].y;
sort(arr+1,arr+1+n,cmp);
ll l=-1e9,r=1e9;
while(l+1<r){ll mid=(l+r)/2;if(check(mid)) l=mid;else r=mid;
}
cout<<l;return 0;
}