cdcq

梦幻小鱼干

导航

【2017中国大学生程序设计竞赛-哈尔滨站】B - K-th Number

原题:

 

题意:

给你一个长度为N的正整数组A,对于这个数组的所有子区间,若长度小于k则不管它,若长度大于等于k则取第k大放入数组B

问你B中第M大的数是谁

 

一眼序列分治,然而没思路

数据结构?能想到从大到小排序,然后小于第i个数的都视为1,用数据结构维护第i个数在多少个区间是第k大

然后就没有然后了……

序列分治和数据结构自闭了两个小时,最后才想起来试试别的思路

比如DP或二分什么的

终于灵稽一动

答案满足二分单调性

二分的答案m越大,[m+1,n]中的数作为第k大的区间的总数量就越大

那么二分一个答案m,问题转化为求[m+1,n]中的数作为第k大的区间的总数量

因为只需要[m+1,n]中的任意一个数作为第k大,而[1,m]的数没有任何用,可以无视

那么可以把大于m的数看成1,小于等于m的数k看成0

问题转化为统计有多少个子区间,使得区间内有k个1

只需要O(n)复杂度,可以双指针,左边指针i每次加1,如果经过1就让区间中1的减1,如果1的个数小于k,就让右边的指针j往右跑到刚好等于k为止,把n-j+1统计到结果即可

注意二分的写法,容易写错

 

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 #define LL long long
 8 int rd(){int z=0,mk=1;  char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-')mk=-1;  ch=getchar();}
10     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
11     return z*mk;
12 }
13 int n,o;  LL m;
14 int a[110000];
15 int b[110000];
16 LL cclt(int x){
17     for(int i=1;i<=n;++i)  b[i]=(a[i]>=x);
18     LL bwl=0;
19     for(int i=1,j=0,k=0;i<=n && j<=n;++i){
20         for(;k!=o && j<n;)  k+=b[++j];
21         if(j>n || (j==n && k!=o))  break;
22         bwl+=n-j+1;
23         k-=b[i];
24     }
25     return bwl;
26 }
27 int bnrsch(int x,int y){
28     int l=x,r=y,md;
29     while(l+1<r){
30         md=(l+r)>>1;
31         (cclt(md)>=m ? l : r)=md;
32     }
33     return cclt(r)>=m ? r : l;
34 }
35 int main(){
36     //freopen("ddd.in","r",stdin);
37     int T;  cin>>T;
38     while(T --> 0){
39         cin>>n>>o>>m;
40         int mx=0,mn=1000000007;
41         for(int i=1;i<=n;++i){
42             a[i]=rd();
43             mx=max(mx,a[i]);
44             mn=min(mn,a[i]);
45         }
46         printf("%d\n",bnrsch(mn,mx));
47     }
48     return 0;
49 }
View Code

 

posted on 2019-10-14 21:52  cdcq  阅读(351)  评论(0编辑  收藏  举报