您的位置:首页 > 其它

解题报告:51nod 1686 第K大区间 二分+尺取

2017-03-14 21:02 357 查看
题目链接:

51nod 1686第k大区间

思路:

可以发现答案范围为1~n,那么考虑二分答案。

check函数可以用尺取的方式,每当r右移,左移l使得当前序列的值小于check的值,同时加上右边全部的序列个数(n-l+1)个,

这样就可以得到序列的值大于等于check的值的序列数。

需要注意的是输入的数组的值在Int范围内,可以直接用map,交了两发,一发T了,一发A了。。

考虑题目只关注数之间的相对大小关系而不关注数本身的值,于是可以将输入的数映射到对应的同样大小次序的数,这样最大的数就压缩到了数组的长度n,便可以直接用数组,不需要再用map,很稳。

代码:

#include<bits/stdc++.h>

using namespace std;

int n;
long long k;
int A[100005];
int B[100005];
int num[100005];

bool check(int x){
memset(num,0,sizeof(num));
long long res = 0;
int mx = 0;
int s = 1 , e = 1;
for(e=1;e<=n;e++){
mx = max(mx,++num[A[e]]);
if(mx==x){
while(mx==x){
res += n-e+1;
num[A[s]]--;
if(num[A[s]]==mx-1){
mx--;
}s++;
}
}
if(res>=k){
return true;
}
}return false;
}

bool cmp(int a,int b){
return A[a]<A[b];
}

int main()
{
while(scanf("%d%I64d",&n,&k)==2){
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
B[i]=i;
}sort(B+1,B+1+n,cmp);
for(int i=1,j=1;i<=n;j++){
int kk = i+1;
while(kk<=n&&A[B[kk]]==A[B[i]])kk++;
while(i<kk){
A[B[i++]]=j;
}
}
int s = 1 , e = n;
while(s<e){
int m = (s+e)>>1;
if(m==s)break;
if(check(m)){
s = m;
}else {
e = m-1;
}
}if(check(e))s=e;
printf("%d\n",s);
}return 0;
}

/*
//直接用map

#include<bits/stdc++.h>

using namespace std;

int n;
long long k;
int A[100005];
map<int,int>num;
bool check(int x){
num.clear();
long long res = 0;
int mx = 0;
int s = 1 , e = 1;
for(e=1;e<=n;e++){
mx = max(mx,++num[A[e]]);
if(mx==x){
while(mx==x){
res += n-e+1;
if(--num[A[s++]]==mx-1){
mx--;
}
}
}
if(res>=k){
return true;
}
}return false;
}

int main()
{
while(scanf("%d%I64d",&n,&k)==2){
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
}int s = 1 , e = n;
while(s<e){
int m = (s+e)>>1;
if(m==s)break;
if(check(m)){
s = m;
}else {
e = m-1;
}
}if(check(e))s=e;
printf("%d\n",s);
}return 0;
}

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: