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; }
沉迷于日日菜醒,不能自拔
posted on 2017-08-04 11:12 仰望咸鱼Orzzzz 阅读(120) 评论(0) 编辑 收藏 举报