2017 Multi-University Training Contest - Team 3 3.Kanade's sum

链接: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 }

 

posted @ 2017-08-03 22:30  7391_KID  阅读(194)  评论(0编辑  收藏  举报