2017 Multi-University Training Contest 3 1003/hdu6058

来源:互联网 发布:淘宝一元拍卖骗局 编辑:程序博客网 时间:2024/06/06 00:39

题意:给你个1-n的排列,找到每一个区间第k大的数,求这些数加起来的总和,如果区间长度小于k,值就为0。

开始一直想用主席树解,但是复杂度太高,不能做,思路卡的时候还是要多转换下思路才行。

解法:找第k大数可以转换为,从小到大枚举x,找一个数x的左边大于x的y个数,右边大于x的k-y-1个数的区间有多少个,然后乘以x然后一起加起来就得到答案了。主要使用双向链表这个神奇的东西,如果前面的数的贡献算完了,那么就可以删除它,因为是从小到大枚举,所以它对后面的区间计算没有贡献。


#include <stdio.h>#include <stdlib.h>#include <cmath>#include <string.h>#include <string>#include <algorithm>#include <map>#include <set>#include <stack>#include <queue>#include <vector>#include <iostream>#define LL long long#define INF 0x3f3f3f3fconst int MAX_N = 5e5+10;const int mod = 100;const double eps = 1e-6;using namespace std;LL a[MAX_N],b[MAX_N];int pos[MAX_N],pre[MAX_N],nxt[MAX_N];int main(){    int T;    scanf("%d",&T);    while(T--)    {        int n,k,x;        scanf("%d%d",&n,&k);        for(int i = 1;i <= n;i++)        {            scanf("%d",&x);            pos[x] = i;            pre[i] = i-1;            nxt[i] = i+1;        }        pre[0] = 0; nxt[n+1] = n+1;        LL ans = 0;        int l,r;        for(int i = 1;i <= n;i++)        {            l = r = 0;            x = pos[i];            for(int j = x;l <= k&&j >= 1;j = pre[j])                a[++l] = j-pre[j];            for(int j = x;r <= k&&j <= n;j = nxt[j])                b[++r] = nxt[j]-j;            LL tmp = 0;            for(int j = 1;j <= l;j++)                if(k-j+1 >=1&&k-j+1 <= r)                    tmp+=a[j]*b[k-j+1];            ans+=tmp*i;            pre[nxt[x]] = pre[x];            nxt[pre[x]] = nxt[x];        }        cout<<ans<<endl;    }    return 0;}/**/