HDU 6040

题意略。

思路:题目就是在询问你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;
}
原文地址:https://www.cnblogs.com/tiberius/p/8637030.html