HDU 6058 Kanade's sum

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6058

题意:

  给一个长度为n(n<=5e5)数组a,a为1-n的一个全排列,然后给一个k(1<=k<=min(n,80)),和一个函数f(l,r,k):区间[l,r]的第k大的数(区间长度小于k时为0).求所有区间的第k大的数的和。

题解:

  记录a中1-n每个数的下标,从小到大依次删除,每次删除时需要维护两个并查集,分别表示当前这个数的左边的第一个数和右边第一个数下标,因为删除是按照从小到大的顺序,所以通过并查集得到的左边的第一个数就是左边第一个比它大的数,然后依次求左边第2-k比它大的数的位置,右边同理。然后求一下这个数的贡献度

sigma((lx[j-1]-lx[j])*(rx[k-j+1]-rx[k-j]))*a[i];

BTW:dalao们都是用链表xjb维护一下就过了,太强了Orz。

 

/*=================================================================
并查集
=================================================================*/
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const double eps = 1e-10;
const int maxn = 5e5 + 100;
int T,n,k,l[maxn],r[maxn],pos[maxn],a[maxn];
int lx[100],rx[100];
int find(int x,int f[]) {
    return x==f[x]?x:f[x]=find(f[x],f);
}
int main() {
#ifdef ac
    freopen("in.txt" , "r" , stdin);
//    freopen("out.txt" , "w" , stdout);
#endif
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;++i) {
            scanf("%d",&a[i]);
            pos[a[i]]=i; l[i]=i; r[i]=i;
        }
        long long ans=0;
        r[n+1]=n+1; l[0]=0; int len=n-k+1;
        for(int i=1;i<=len;++i) {
            lx[0]=pos[i],rx[0]=pos[i];
            for(int j=1;j<=k;++j) {
                if(lx[j-1])lx[j]=find(lx[j-1]-1,l);
                if(rx[j-1]!=n+1)rx[j]=find(rx[j-1]+1,r);
                else rx[j]=n+1;
            }
            for(int j=1;j<=k;++j){
                if(lx[j-1]) {
                    if(rx[k-j]==n+1)continue;
                    ans+=(long long)(lx[j-1]-lx[j])*(rx[k-j+1]-rx[k-j])*i;
                }
                else break;
            }
            l[lx[0]]=l[lx[0]-1]; r[rx[0]]=r[rx[0]+1];
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

 

 

 

 

 

posted on 2017-08-04 11:12  仰望咸鱼Orzzzz  阅读(120)  评论(0编辑  收藏  举报

导航