Difference (第k大(二分)+双指针+ST表)+(很多小细节!!!!!) (MINIEYE杯十六届)
题目:D-Difference_MINIEYE杯第十六届华中科技大学程序设计邀请赛 (nowcoder.com)
大意: 求第k大的 函数值 (区间内的最大差值)*(区间长度)
思路:
- 首先遇到第k大的什么值,就用二分答案,看看有几个比这个答案大的数
- 另外可以看到, 区间 函数值(L,R) 是单调的, VAL(L,R)> VAL(L-1,R)||VAL(L,R-1),因为就算差值不变,区间长度也会减小,
- 既然这个区间满足单调性,就可以利用双指针,来找有哪些区间比这个值大或者小,
- 这里 我选择的小 比较好理解, 那么利用减法原则,第k大可以转化为第K小 (所有值的数量-k+1;
- 双指针:找有哪些数量的值比他小, 然后这个数量+1,就是第几大.
- 找到所有比 a 小的 区间 (以L端点为极限值的), 就是 L-1,那么这个区间就大于 a 了, 因此 R到L 内的都是小的, 那么 L--R,L+1--R,L+2--R的子区间都是比他小哒
- 而且我们是 从小到大枚举 R,所以 L到R-1这个区间已经被计算了,不用担心. zh
- 何时移动 L呢? 就是这个区间已经大于等于a了, 就要移动了,L++,L也是从小到大枚举,而且这是区间单调,一定可以保证正确性和完整性
- 二分判断的时候,要注意找到的值一定是这个数列里面有的,因此一定要搞到边缘值, 比如 数列 1 ,3 || 2 是第二大,3也是第二大,4,就是第3大了,3就是边缘值.

#include <bits/stdc++.h> using namespace std; #define ri int #define M 500007 long long m; int n; int p[M]; int mx[M][19]; int mi[M][19]; int lg2[M]; void ST() { for(int i = 2; i <= n; i++){ lg2[i] = lg2[i >> 1] + 1; } for(ri i=1;i<=n;i++) mi[i][0]=p[i],mx[i][0]=p[i]; for(ri i=1;i<=lg2[n];i++) { for(ri j=1;j+(1<<i)-1<=n;j++) { mi[j][i]=min(mi[j][i-1],mi[j+(1<<(i-1))][i-1]); mx[j][i]=max(mx[j][i-1],mx[j+(1<<(i-1))][i-1]); } } } long long qu(int a,int b) { int k=lg2[b-a+1]; long long mmx=max(mx[a][k],mx[b-(1<<k)+1][k]); long long mmi=min(mi[a][k],mi[b-(1<<k)+1][k]); return (mmx-mmi)*(b-a+1); } long long ck(long long a) { long long ans=0; for(int i=1,j=1;j<=n;j++) { while(i<=j&&qu(i,j)>=a) { i++; } ans+=(j-i+1); } ans++; return ans<=m; } int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n>>m; m=1ll*n*(n+1)/2-m+1; for(ri i=1;i<=n;i++) { cin>>p[i]; } ST(); long long l=0,r=qu(1,n)*n; long long ans=0; while(l<=r) { long long mid=(l+r)>>1; if(ck(mid)) { ans=mid; l=mid+1; } else r=mid-1; } cout<<ans; return 0; }
双指针模板:
- 遇到要求出说有的区间满足情况个数
- 且区间时单调的
- 因此只需要求出极限的区间范围,内部的区间,直接(r-l+1);
- 注意下面这个是 i 到 j 里面是合理外面就不合理了, 越往外面扩展,就越可能不合理
- 然后 j每次加一, 这个贡献就要加上当前的区间长度. 在处理的那个地方

for(int i=1,j=1;j<=n;j++) { while(i<=j&&ck(i,j)) // 弄走不想要的情况 { // 一定要特别主要 i==j时有些题目情况的特判 i++; } 进行处理 }
好了,接下来,细节上的重头戏,耽搁了我4-5h找这些错
- 首先还是要主要数据范围的大小int还longlong, (m是longlong,没太注意)
- 记住!!! 大数据或者二维数组,的那种 一定要看 是int 还是 long long, 500005 20,用long long 就 G
- 所以在用long long 时 一定要关注这个范围大小,特别时二维数组本题就是 ST表上 后面跟了一个 20就每太管
- int 10^7 顶天了,可以方向用
- 当然在有long long的题时,一定要看每一个步骤有没有超过int,超了就 1ll
- 不过对于单个元素,用long long 没有问题,就不用每一步去看他超int每,对于二维数组一定不能图方便
- 其次调用系统函数log2 的时间复杂度时 log的 ,不是O1,因此用ST表一定要自己写一个log2数组
- 二分答案时一定要注意 L是不是小于0的,等于0的,!!!! 很重要!!!!
- 用cin.tie(0)时,一定不用用printf, 自己写习惯了,用cout,写cin时,他的那个啥 关闭同步流一定要记得写
- 写完intmain,就写,养成习惯