Problem
Solution
题意:给定数列{ ai a i },求 k k 个/子串/的/子串和/的/和,其中k个子串满足:每个子串长度, k k 个子串互不相同
这题求解的是个不同的值相加,总值最大,看上去有点像SDOI-2010-魔法猪学院- k k 短路(这两题好像是同一年的),所以根据后者的解法,能比较自然地想到使用堆求解(对情况建立大根堆,弹出前个)
然后还有堆内元素的转换( k k 短路中是删除一个点的状态,增加从该点扩散的点的状态),可以用表示左端点在 i i ,右端点在,在当前左右端点情况下最大前缀和(从 ai a i 算起),每次取出这种最优情况,然后将这种情况劈开
从 pos p o s 劈开,
(i,l,r,pos) ( i , l , r , p o s )
劈成
(i,l,pos−1,query(l,pos−1)) ( i , l , p o s − 1 , q u e r y ( l , p o s − 1 ) )
和
(i,pos+1,r,query(pos+1,r)) ( i , p o s + 1 , r , q u e r y ( p o s + 1 , r ) )
其中 query(x,y) q u e r y ( x , y ) 是 [x,y] [ x , y ] 前缀和极大值的位置
可以使用线段树或 st s t 表,推荐 st s t 表,没卡线段树,也行
详见代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rg register
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define get_(x,y) (s[(x)]>s[(y)]?(x):(y))
#define cl(x) memset(x,0,sizeof(x))
template <typename _Tp> inline void read(_Tp&x){
rg char c11=getchar(),ob=0;x=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return ;
}
const int N=505000,LOG=20;
int st[N][LOG],s[N],Log[N];
int n,L,R,K;
struct data{int i,l,r,pos;};
void init();
inline int query(int l,int r){
if(l==r)return l;
int pos=Log[r-l+1];
return get_(st[l][pos],st[r-(1<<pos)+1][pos]);
}
priority_queue <data,vector<data> > q;
inline char operator < (data x,data y){return s[x.pos]-s[x.i-1]<s[y.pos]-s[y.i-1];}
void work(){
data now,r;
rg ll ans=0;
for(rg int i=1;i+L-1<=n;++i)
now.i=i,
now.l=i+L-1,
now.r=min(n,i+R-1),
now.pos=query(i+L-1,min(n,i+R-1)),
q.push(now);
while(K--){
data now=q.top();q.pop();
ans+=s[now.pos]-s[now.i-1];
if(now.l<now.pos)
r.i=now.i,
r.l=now.l,
r.r=now.pos-1,
r.pos=query(now.l,now.pos-1),
q.push(r);
if(now.pos<now.r)
r.i=now.i,
r.l=now.pos+1,
r.r=now.r,
r.pos=query(now.pos+1,now.r),
q.push(r);
}
printf("%lld\n",ans);
return ;
}
int main(){
init();
work();
return 0;
}
void init(){
read(n),read(K),read(L),read(R);
for(rg int i=1;i<=n;++i) read(s[i]),s[i]+=s[i-1];
Log[0]=-1;
for(rg int i=1;i<=n;++i) Log[i]=Log[i>>1]+1,st[i][0]=i;
for(rg int i=n;i;--i)
for(rg int j=1;i+(1<<j)-1<=n;++j)
st[i][j]=get_(st[i][j-1],st[i+(1<<(j-1))][j-1]);
return ;
}