补了下前置技能……
题意就是求一段区间的权值和前k大的子序列的和。
把段扔进优先队列
每次拿出来之后按照所选择的j进行分裂
#include<bits/stdc++.h> #define N 500005 #define mp(a,b,c,d) (seg){a,b,c,d} using namespace std; typedef long long ll; struct seg{int i,l,r,t;}; int bin[20],Log[N],n,k,l,r,a[N],st[20][N]; ll ans=0; void initrmq(){ Log[0]=-1;for(int i=1;i<=n;i++)Log[i]=Log[i>>1]+1; for(int i=1;i<=n;i++)st[0][i]=i; for(int i=n;i;i--)for(int j=1;j<=18;j++) if(i+bin[j]-1<=n){ int t1=st[j-1][i],t2=st[j-1][i+bin[j-1]]; st[j][i]=a[t1]>a[t2]?t1:t2; } else break; } inline int query(int l,int r){ if(l==r)return l;int t=Log[r-l+1]; int t1=st[t][l],t2=st[t][r-bin[t]+1]; return a[t1]>a[t2]?t1:t2; } inline bool operator <(seg x,seg y){ return a[x.t]-a[x.i-1]<a[y.t]-a[y.i-1]; } inline int read(){ int f=1,x=0;char ch; do{ch=getchar();if(ch==‘-‘)f=-1;}while(ch<‘0‘||ch>‘9‘); do{x=x*10+ch-‘0‘;ch=getchar();}while(ch>=‘0‘&&ch<=‘9‘); return f*x; } int main(){ bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]<<1; n=read();k=read();l=read();r=read(); for(int i=1;i<=n;i++)a[i]=read()+a[i-1]; initrmq(); priority_queue<seg,vector<seg> >q; for(int i=1;i<=n;i++)if(i+l-1<=n){ int t=min(n,i+r-1);q.push(mp(i,i+l-1,t,query(i+l-1,t))); } for(int i=1;i<=k;i++){ seg t=q.top();q.pop(); ans+=a[t.t]-a[t.i-1]; if(t.t-1>=t.l)q.push(mp(t.i,t.l,t.t-1,query(t.l,t.t-1))); if(t.t+1<=t.r)q.push(mp(t.i,t.t+1,t.r,query(t.t+1,t.r))); } cout<<ans<<endl; }
时间: 2024-10-27 07:56:12