【思维-链表】hdu 6058

来源:互联网 发布:湖南才能网络 编辑:程序博客网 时间:2024/06/17 02:04

Kanade's sum

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2633    Accepted Submission(s): 1095


Problem Description
Give you an array A[1..n]of length n
Let f(l,r,k) be the k-th largest element of A[l..r].
Specially , f(l,r,k)=0 if rl+1<k.
Give you k , you need to calculate nl=1nr=lf(l,r,k)
There are T test cases.
1T10
kmin(n,80)
A[1..n] is a permutation of [1..n]
n5105
Input
There is only one integer T on first line.
For each test case,there are only two integers n,k on first line,and the second line consists of n integers which means the array A[1..n]
Output
For each test case,output an integer, which means the answer.
Sample Input
1
5 2
1 2 3 4 5
Sample Output
30
 
题意:输入n,k;求出1~n各个区间第k大的值的和;

思路:把各个数字(Ai)作为第k大的值,看他能在多少个区间(x)作为第k大,则这个数字的贡献为:Ai * x

怎么计算各个数字作为第k大的区间有多少个呢?   假设这个数前有 x 个比他大的,则后面有k-x-1个比他大的来确保他是第k大;

一开始先维护一个满的链表,然后从小到大删除,每次算完一个数,就在链表里面删除,算x的时候,已保证删除的数都比x小,都可以用来算贡献。i 和 pre[i] 和nxt[i] 的距离就是小于当前的数的数目+1,距离分别由a,b保存。

给个样例,

6 2

2 1 4 3 6 5

假如 以1作为第k大 来求其所在区间,模拟:

pos      1        2      3      4     5    6

       a2 2 a11 b14b23  6  5  (跳k下就够了)  

1作为第k大所在的区间就是:a1*b2+a2*b1;

代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const int N=1000006;int pre[N+10],nex[N+10],pos[N+10],v[N+10];int a[N+10],b[N+10];int n,k;ll solve(int x){    int c1=0,c2=0;    for(int i=x;i&&c1<k;i=pre[i])        a[++c1]=i-pre[i];    for(int i=x;i<=n&&c2<k;i=nex[i])        b[++c2]=nex[i]-i;    ll ans=0;    for(int i=1;i<=c1;i++)    {        if(k-i+1<=c2&&k-i+1>=1)            ans+=a[i]*b[k-i+1];    }    return ans;}void del(int x){    nex[pre[x]]=nex[x];    pre[nex[x]]=pre[x];}int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&n,&k);        for(int i=1; i<=n; i++)        {            scanf("%d",&v[i]);            pos[v[i]]=i;        }        for(int i=0;i<=n+1;i++)            pre[i]=i-1,nex[i]=i+1;        pre[0]=0,nex[n+1]=n+1;        ll ans=0;        for(int i=1;i<=n;i++)        {            ans+=solve(pos[i])*i;            del(pos[i]);        }        printf("%I64d\n",ans);    }    return 0;}
//很巧妙的链表,从小到大计算贡献完,然后删除,并不影响接下来比它的数字计算贡献;



参考blog:点击打开链接

原创粉丝点击