hdu6058

来源:互联网 发布:adobe ld cc mac 编辑:程序博客网 时间:2024/06/16 00:53

题意给你一个n的排列,问所有区间第k大的数的和是多少
n为5e5,k为80
思路对于每一个i,要找到他前面k个大的,要找到后面k个大的。这样,i就是这些区间中第k大的(其实就是最小的)。但是这种操作你需要枚举每一个数。复杂度为On。这样就不行,然后骚操作来了。对于每一个数,记录他比要查的数大的前一个位置,记录比要查的数大的数后一个位置,这样每次最多跳80次,这样复杂度就够了。然后这就是链表,然后就是用数组来模拟链表。pre表示上一个位置,nxt表示下一个位置。先算小的,算完后删掉,这样再更新pre和nxt的时候就更新就可以了。还有为什么要乘。
**例子**4 1 2 3,k为2,然后,就有两个区间,412 , 123,就是这样。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <set>#include <map>#include <string>#include <cmath>#include <cstdlib>#include <ctime>using namespace std;typedef long long LL;const int N = 5e5 + 10;int n , k ;LL ans;int a[N] , b[N] , nxt[N] , pre[N] , pos[N];void yu(){    for(int i = 1 ; i <= n ; i++){        pre[i] = i - 1;        nxt[i] = i + 1;    }}LL Getans(int x){    LL ret = 0;    int r1 = 0 , r2 = 0;    for(int i = x ; i >= 0 && r1 <= k ; i = pre[i]) a[++r1] = i - pre[i];    for(int i = x ; i <= n && r2 <= k ; i = nxt[i]) b[++r2] = nxt[i] - i;    for(int i = 1 ; i <= r1 ; i++){        if(k - i + 1 >= 1 && k - i + 1 <= r2)            ret += a[i] * b[k - i + 1];    }    return ret;}void del(int x){    int nx = nxt[x];    pre[nx] = pre[x];    nxt[pre[x]] = nx;}void solve(){    for(int i = 1 ; i <= n + 1 ; i++){        ans = ans + Getans(pos[i]) * i;    //  printf("ans = %d\n",ans);        del(pos[i]);    }    printf("%lld\n",ans);}int main(){    //freopen("in.txt","r",stdin);    //freopen("out.txt","w",stdout);    int T;    scanf("%d",&T);    while(T--){        ans = 0;        scanf("%d %d", &n , &k);        yu();        for(int i = 1 ; i <= n ; i++){            int temp;            scanf("%d",&temp);            pos[temp] = i;        }        solve();    }    return 0;}
原创粉丝点击