您的位置:首页 > 其它

2017 多校训练第一场 HDU 6040 Hints of sd0061

2017-07-26 17:40 423 查看
题意:
给你一个长度为n的序列,序列由题面给的函数生成。然后m次询问,询问这个序列上第bi小的数。

分析:

n最大达到1e7,单纯的快排是不行的。但是可以利用快排的思想,每次找到一个划分,序列中比划分值小的数都在划分的左侧,反之则在右侧,这样每阶段都可以省一段时间。题面给的函数返回类型是unsigned,这个也需要注意。

这就需要一个利器的STL了,C++中的nth_element(arr,arr+k,arr+n),用法是将长度为n的数组arr进行划分,第k位置上就是第k+1大的数(下标从0开始算,也即有k个数比arr[k]小),最重要的是这个函数近似线性!!!

注意:

因为输入保证任意两个小的之和小于第三个

所以查询数列的间隔一定大于等于斐波那契

也就是从大到小查询的话,每次至少能去掉一半的区间

#include <bits/stdc++.h>
#define test TEST
using namespace std;

typedef long long ll;
const int maxn=1e7+7;
int b[105],id[105];
unsigned ans[105];

bool vis[maxn];
unsigned a[maxn];
unsigned x,y,z;
unsigned rng61() {
unsigned t;
x ^= x << 16;
x ^= x >> 5;
x ^= x << 1;
t = x;
x = y;
y = z;
z = t ^ x ^ y;
return z;
}

bool cmp(const int &xx,const int &yy){
return b[xx]>b[yy];
}
int main(int argc, char const *argv[])
{
ll n,m;
int cas=1;
while(~scanf("%lld %lld %u %u %u",&n,&m,&x,&y,&z)){
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++){
scanf("%d",&b[i]);
id[i]=i;
}
for(int i=0;i<n;i++){
a[i]=rng61();
}
sort(id,id+m,cmp);//挑选询问大的编号放在前面,可以降低复杂度
int p,last=n;
for(int i=0;i<m;i++){
p=id[i];
nth_element(a,a+b[p],a+last);
ans[p]=a[b[p]];
last=b[p];
}
//输出答案
printf("Case #%d:",cas++);
for(int i=0;i<m;i++){
printf(" %u",ans[i]);
}printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: