链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1003&cid=761
题意:给定1~n的一个排列,定义f(l,r,k)为区间[l,r]第k大的数,不存在第k大的数时定义为0,给定k,求所有区间的f的和。
分析:考虑每个数被取到的次数,i左右各取k个比i大的数,位置记为a1,a2,...,ak和b1,b2,...,bk,i的位置为idx,计算(bk-b(k-1))*(idx-ak),然后往左移一个,继续算,求和后乘i,枚举i求和即为答案,不存在k个比i大的数时就当作已经移到了相应的位置。
取比i大的数时,可以先取i=1,然后把1删掉,再取i=2,这样保证左右的数都是比i大的,具体可以用并查集来操作,整个复杂度就是O(nkT)。
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<algorithm> 5 #include<cstring> 6 using namespace std; 7 const int maxn=5e5+5; 8 typedef long long ll; 9 int loca[maxn],a[maxn],p[maxn],q[maxn]; 10 int n,k; 11 ll ans; 12 int Findp(int x){return x==p[x]?x:p[x]=Findp(p[x]);} 13 int Findq(int x){return x==q[x]?x:q[x]=Findq(q[x]);} 14 void Delete(int k){ 15 p[k]=k-1; 16 q[k]=k+1; 17 } 18 void solve(){ 19 ans=0; 20 for(int i=0;i<=n+1;i++){ 21 p[i]=i;q[i]=i; 22 } 23 for(int i=1;i<=n-k+1;i++){ 24 ll count=0; 25 int t=loca[i],l=t,r=t,c=0; 26 while(c<k-1&&r<n&&Findq(r+1)<=n){ 27 r=Findq(r+1);c++; 28 } 29 while(c<k-1){ 30 l=Findp(l-1); 31 c++; 32 } 33 int r0,l0; 34 while(r>=t){ 35 r0=min(n+1,Findq(r+1));l0=Findp(l-1); 36 count+=(ll)(r0-r)*(l-l0); 37 r=Findp(r-1);l=l0; 38 if(l==0||r<t)break; 39 } 40 ans+=i*count; 41 Delete(t); 42 } 43 } 44 int main(){ 45 //freopen("e:\in.txt","r",stdin); 46 int t; 47 scanf("%d",&t); 48 while(t--){ 49 scanf("%d%d",&n,&k); 50 for(int i=1;i<=n;i++){ 51 scanf("%d",&a[i]); 52 loca[a[i]]=i; 53 } 54 solve(); 55 cout<<ans<<endl; 56 } 57 return 0; 58 }