题意略。
思路:题目就是在询问你m次,第k小是哪个数。首先我们可以想到直接排序后,即可O(1)来查找询问。但是题目中n的范围给的是1e7,
无法承受nlogn的复杂度。从而想到另外一种求静态第k小的方法:利用快速排序来做到。时间复杂度是O(n),但是询问次数m是100,
同样无法承受O(n * m)的复杂度。这时我们应该想到题目中给的另外一个条件:if (bi < bk && bj < bk && bi != bk) then bi + bj <= bk。
从而我们知道了询问次数最坏的情况下bi数列应该是一个斐波那契数列,而斐波那契数列有一个重要性质:b[n] / b[n+1] = 0.618。如果
我们倒着来求这些询问的答案,那么后一个结果就可以利用上前一个结果来缩短自己的划分范围。我们知道第一个结果的求解复杂度是
O(n)的,那么总的复杂度是:n * (1 + 0.618 + 0.618^2 + 0.618^3 +......) = O(n)。
详见代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 1e7 + 5; struct query{ int bi,id; unsigned ans; }; unsigned store[maxn]; query depot[105]; int n,m,cas = 1; 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 query& a,const query& b){ return a.id < b.id; } bool cmp2(const query& a,const query& b){ return a.bi < b.bi; } int main(){ while(scanf("%d%d%u%u%u",&n,&m,&x,&y,&z) == 5){ for(int i = 0;i < m;++i){ scanf("%d",&depot[i].bi); depot[i].id = i; } for(int i = 0;i < n;++i){ store[i] = rng61(); } sort(depot,depot + m,cmp2); int bound = n; for(int i = m - 1;i >= 0;--i){ nth_element(store,store + depot[i].bi,store + bound); depot[i].ans = store[depot[i].bi]; bound = depot[i].bi; } sort(depot,depot + m,cmp); printf("Case #%d:",cas++); for(int i = 0;i < m;++i){ printf(" %u",depot[i].ans); } printf(" "); } return 0; }