HDU 6040& 2017年多校训练第一场 1008题
来源:互联网 发布:缤特力升级软件 编辑:程序博客网 时间:2024/06/06 02:41
2017年多校训练第一场 1008题
There are
The coach asks constroy to make a list of contestants. constroy looks into these hints and finds out:
Now, you are in charge of making the list for constroy.
For each test case:
The first line contains five integers
The second line contains
The
unsigned x = A, y = B, z = C;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;}
3 3 1 1 10 1 22 2 2 2 21 1
Case #1: 1 1 202755Case #2: 405510 405510
题目大意:隔壁大佬一句话总结就是,O(N)时间内求第K大。
他还加了一句,我找到网上的板了,然后我们就没一个人管他了,让他自生自灭了。结果是,GG。
首先题目会给你一串乱序数字,数据规模达到了 1e7 ,然后最多有100个询问。100 * 1e7 = 1e9 大概是 10s, 所以简单的O(N)查找肯定GG了。
注意到题目里面有个条件:constroy looks into these hints and finds out:
对于询问有个快速上升的趋势,后面的询问 K 如果大于前面的询问,则K要必前面询问中任意两个数值的和大。
经过一个简单的推理,就差不多是: 0,1,2,3,5,8,......在1e7范围内,这个上升速度也是极快的,反正不用到第一百个数就比1e7大了。
所以,由此 可见,我们需要的询问数量比log2(1e7)多那么一点点,然后我们从大范围一个一个找到小范围,最后就能近似于O(N)的时间内完成了。
正确的解法:(perfect)
#include <bits/stdc++.h>using namespace std;const int maxn = 1e7 + 9;unsigned a[maxn], ans[105];int b[105], id[105];int n, m;unsigned x, y, z;inline 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;}void change(unsigned & a, unsigned & b) {unsigned t;t = a; a = b; b = t;}int Rand(int l, int r) {int tmp = rand() % (r - l + 1) + l;change(a[l], a[tmp]); unsigned key = a[l]; int i = l + 1, j = r; while(i <= j) { while((i <= r) && a[i] <= key) i++; while((j >= l) && a[j] > key) j--; if(i < j) change(a[i], a[j]); } /* int cnt = l; for(int i = l + 1; i <= r; i++) { if(a[i] < a[l]) change(a[++cnt], a[i]); } change(a[l], a[cnt]);return cnt; */ change(a[l], a[j]); return j;}unsigned check(int l, int r, int k) {int pos;while(k != (pos = Rand(l, r))) {if(k < pos) r = pos - 1;else l = pos + 1;}return a[k];}int cmp(int x, int y) {return b[x] < b[y];}int main() { //freopen("1008.in", "r", stdin); //freopen("out.out", "w", stdout);srand(time(NULL));for(int kase = 1; ~scanf("%d%d%u%u%u", &n, &m, &x, &y, &z); kase++) {for(int i = 1; i <= n; i++) { a[i] = rng61();}for(int i = 1; i <= m; i++) {scanf("%d", &b[i]); b[i]++; id[i] = i;}sort(id + 1, id + 1 + m, cmp);ans[id[m]] = check(1, n, b[id[m]]); for(int i = m - 1; i > 0; i--) {if(b[id[i]] == b[id[i + 1]]) {ans[id[i]] = ans[id[i + 1]];continue;}ans[id[i]] = check(1, b[id[i + 1]], b[id[i]]);}printf("Case #%d: ", kase);for(int i = 1; i <= m; i++) printf("%u%c", ans[i], i == m ? '\n' : ' ');}return 0;}
然后我再介绍一个被随机数看命运的一个写法:
如果按照官方思路,先找BK,然后缩区间找B(K - 1) 然后继续缩,一直到结束。但是对于第B(K - 1)大的数,我们在找第BK大的数的时候,可能一句找到了一个第M大的数,这个数比第B(K - 1)小。如果这样的话,我们直接在M和BK之间搜第B(K - 1)就好了。理论上来讲,应该是比每次都是缩区间然后整个区间查询的速度要快一点的。
想法说完了,说方案:
我用了一个数组来映射需要查询的第K大的数,也就是sum[question[i] + 1] = 1,然后处理sum数组,求前缀和。区间[ i, j ]是否需要去查询的判断条件就是sum[j] - sum[i - 1] 是不是大于0,大于0代表这个区间内有需要查询的,否则,这块乱序的数字就不用管了。
对于我自己的思路,我直接改了快排的实现,快排是需要对当前分出来的左右区间都要去查询一遍,这里只要在查询之前加个判断,就可以避开不必要的查询。理想状态下应该是比标程更优的。
然而,可能是自己手写的快排真的太靠天意了,在随机数的影响下,按这个思路写出来的程序运行时间在 2.10 - 2.80之间徘徊。(哇,天知道为什么会这样。)补题的时候恶意提交了100发,大约过了50发,另外50发T了,可能这就是看脸的游戏了吧。/(ㄒoㄒ)/
代码如下:
#include <bits/stdc++.h>using namespace std;inline void change(unsigned & a, unsigned & b) {unsigned c;c = a; a = b; b = c;}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;}int n, m;const int maxn = 1e7 + 50;unsigned A, B, C;unsigned num[maxn];int sum[maxn], question[105];int cnt;void solve(int l, int r) {if(l < r) {//int k = (l + r) >> 1;int k = rand() % (r - l + 1) + l;change(num[l], num[k]);//int k = l;unsigned key = num[l];int i = l + 1, j = r;while(i <= j) {while((i <= r) && num[i] <= key) i++;while((j >= l) && num[j] > key) j--;if(i < j) change(num[i], num[j]);}change(num[l], num[j]);if(sum[j - 1] - sum[l - 1]) solve(l, j - 1);if(sum[r] - sum[j]) solve(j + 1, r);}}int main() { //freopen("1008.in", "r", stdin); //freopen("out.out", "w", stdout);int kase = 0;srand(time(NULL));while(~scanf("%d%d%u%u%u", &n, &m, &A, &B, &C)) { cnt = 0;x = A, y = B, z = C;memset(sum, 0, sizeof(sum));for(int i = 1; i <= n; i++) num[i] = rng61();for(int i = 1; i <= m; i++) {scanf("%d", &question[i]);sum[question[i] + 1] = 1;}for(int i = 1; i <= n; i++) sum[i] += sum[i - 1];solve(1, n);printf("Case #%d:", ++kase);for(int i = 1; i <= m; i++) {printf(" %u", num[question[i] + 1]);}printf("\n");}return 0;}
- HDU 6040& 2017年多校训练第一场 1008题
- (2017多校训练第一场)HDU
- (2017多校训练第一场)HDU
- (2017多校训练第一场)HDU
- (2017多校训练第一场)HDU
- HDU 6044& 2017年多校训练第一场 1012题
- 2017 多校训练第一场 HDU 6034 Balala Power!
- 2017 多校训练第一场 HDU 6038 Function
- 2017 多校训练第一场 HDU 6035 Colorful Tree
- 2017 多校训练第一场 HDU 6044 Limited Permutation
- 2017多校训练第一场
- 2017多校训练第一场
- 2017 多校训练第一场 HDU 6040 Hints of sd0061
- (2017多校训练第二场)HDU
- (2017多校训练第二场)HDU
- (2017多校训练第二场)HDU
- (2017多校训练第二场)HDU
- (2017多校训练第三场)HDU
- bzoj2741[FOTILE模拟赛L]
- Comparable接口实现自定义对象的排序功能
- (转)你事业的上限究竟在哪里?《哈佛商业评论》史上最佳文章
- hdu2027 统计元音(C语言)
- [二分图匹配] [NOI2009] BZOJ 1562——序列变换
- HDU 6040& 2017年多校训练第一场 1008题
- 一年经验Java程序员面试经
- tcp 通信 服务器端
- 那一战
- SHA1
- HDU 1556 Color the ball(线段树|树状数组)
- Maximum Product
- Ubuntu16.04下安装最新版本的CMake
- echarts颜色渐变