hdu 6058
题意:求任意区间第k大之和
思路:该题因为每个数不重复,如果以X为第k大,我们是不是知道比他大的那些数字的位置,然后从其左边取x个,右边取y个,使得x+y=k-1,即可
所以我们从大到小,把其位置从小到大连接起来,第x个数字,我就从该位置前面去选,后面去选,最多移动k次
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=500005; 5 6 int n,k,pos[N],l[N],r[N],L[N],R[N]; 7 set<int > a; 8 set<int >:: iterator it,itt; 9 10 void hh(int x,int y){//位置的连接 11 int z=L[x];//没插入前,x的前面一个数 12 L[y]=z;//变成y前面的了 13 R[y]=x;//x前面变成y了 14 L[x]=y; 15 R[z]=y;//z后面变成y了,原来是x的 16 } 17 int main(){ 18 int t; 19 scanf("%d",&t); 20 while(t--){ 21 a.clear(); 22 scanf("%d%d",&n,&k); 23 int x; 24 for(int i=1;i<=n;i++) { 25 scanf("%d",&x); 26 pos[x]=i; 27 } 28 a.insert(0);a.insert(n+1); 29 L[0]=-1;R[0]=n+1; 30 L[n+1]=0;R[n+1]=-1; 31 for(int i=n;i>n-k+1;i--){ 32 it=a.lower_bound(pos[i]); 33 34 hh(*it,pos[i]); 35 a.insert(pos[i]); 36 } 37 ll sum=0; 38 for(int i=n-k+1;i>=1;i--){ 39 it=(a.lower_bound(pos[i])); 40 int po=*it; 41 // cout<<po<<endl; 42 for(int j=0;j<=k;j++) l[j] = -1; 43 l[0]=pos[i]; 44 for(int j=1,h=L[po];j<=k&&h!=-1;j++,h=L[h]) { 45 l[j]=h; 46 } 47 r[0]=pos[i]; 48 for(int j=1,h=po;j<=k&&h!=-1;j++,h=R[h]) { 49 r[j]=h; 50 if(l[k-j+1]!=-1) 51 sum+=1LL*i*(l[k-j]-l[k-j+1])*(r[j]-r[j-1]); 52 } 53 hh(*it,pos[i]); 54 a.insert(pos[i]); 55 } 56 cout<<sum<<endl; 57 } 58 }