CF280D k-Maximum Subsequence Sum【线段树】
碎碎念
刚开始:这怎么是个黑题啊,就是一个线段树而已啦。
然后,这道题我断断续续调了两天。
所以说,\(flag\)不要立得太早。
题目解析
如果题目没有说选\(k\)段不相交,而是只有一段的话,那就直接求最大子段和。
但是这道题可以选\(k\)段的话,就相当于我们可以在最大子段和里断开\(k-1\)处(\(k-1\)段),把一些会拖累答案的段退回去不要。
考虑反悔贪心。
我们取一个最大子段和出来,然后把选中的区间取反。
重复这样的操作\(k\)次就可以得到答案。
为什么呢?
如果这一次选中的区间与之前选过的(取反了的)区间没有交集,相当于我们再选了新的一段。
如果与之前选过的有交集,而我们之前又取了反,那就相当于把原来选的那一段给退了回去。如果两段区间(指新选的区间和原来选过的区间)不是包含关系,显然段数会多\(1\)。如果是包含关系,就相当于把原来的区间退回去,而新选的区间裂开了,所以区间总数还是多\(1\)。
如果把一个区间取反,最大子段和变最小子段和(的相反数) 。
所以同时维护一个最小子段和
或者写两个线段树 :一个维护正常的,一个维护相反数。
如果修改,就交换这两个线段树的对应结点(感觉这种好写多了,本来刚开始维护了最小子段和,但最后还是屈服喏\(qwq\),并且优化了一下写法,把原来丑陋无比的\(200+\)的代码压得好看一点了。
►Code View
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define N 100005
#define MOD 998244353
#define INF 0x3f3f3f3f
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
return f*x;
}
int n,k;
pair<int,int> fx[N];
int cnt;//记录下来被修改(取反)过的区间 每次查询时要返回来
int a[N];
struct node{
int sum,mxs,lmx,rmx;
int l,r,rr,ll;
}tree[2][N<<2];
bool tag[N<<2];//tag拿出来 合并的时候就不用记下来再搞回去
node Merge(node x,node y)
{
node t;
t.sum=x.sum+y.sum;
t.mxs=t.lmx=t.rmx=-INF;
if(x.lmx>t.lmx) t.lmx=x.lmx,t.rr=x.rr;
if(x.sum+y.lmx>t.lmx) t.lmx=x.sum+y.lmx,t.rr=y.rr;
if(y.rmx>t.rmx) t.rmx=y.rmx,t.ll=y.ll;
if(y.sum+x.rmx>t.rmx) t.rmx=y.sum+x.rmx,t.ll=x.ll;
if(x.mxs>t.mxs) t.mxs=x.mxs,t.l=x.l,t.r=x.r;
if(y.mxs>t.mxs) t.mxs=y.mxs,t.l=y.l,t.r=y.r;
if(x.rmx+y.lmx>t.mxs) t.mxs=x.rmx+y.lmx,t.l=x.ll,t.r=y.rr;//Cao 这里打成x.rr
return t;
}
void PushUp(int i)
{
tree[0][i]=Merge(tree[0][i<<1],tree[0][i<<1|1]);
tree[1][i]=Merge(tree[1][i<<1],tree[1][i<<1|1]);
}
void Init(int i,int l,int val)
{
tree[0][i].sum=tree[0][i].mxs=tree[0][i].lmx=tree[0][i].rmx=val;
tree[1][i].sum=tree[1][i].mxs=tree[1][i].lmx=tree[1][i].rmx=-val;
tree[0][i].l=tree[0][i].r=tree[0][i].rr=tree[0][i].ll=l;
tree[1][i].l=tree[1][i].r=tree[1][i].rr=tree[1][i].ll=l;
}
void Build(int i,int l,int r)
{
if(l==r)
{
Init(i,l,a[l]);
return ;
}
int mid=(l+r)>>1;
Build(i<<1,l,mid);
Build(i<<1|1,mid+1,r);
PushUp(i);
}
void Shift(int i)
{
swap(tree[0][i],tree[1][i]);
tag[i]^=1;
}
void PushDown(int i)
{
if(tag[i]==1)
{
Shift(i<<1);
Shift(i<<1|1);
tag[i]=0;
}
}
void Update(int i,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
{
Shift(i);
return ;
}
PushDown(i);
int mid=(l+r)>>1;
if(ql<=mid) Update(i<<1,l,mid,ql,qr);
if(qr>mid) Update(i<<1|1,mid+1,r,ql,qr);
PushUp(i);
}
node Query(int i,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return tree[0][i];
PushDown(i);
int mid=(l+r)>>1;
if(qr<=mid) return Query(i<<1,l,mid,ql,qr);
else if(ql>mid) return Query(i<<1|1,mid+1,r,ql,qr);
return Merge(Query(i<<1,l,mid,ql,qr),Query(i<<1|1,mid+1,r,ql,qr));
}
void Modify(int i,int l,int r,int pos,int val)
{
if(l==r)
{
Init(i,l,val);
return ;
}
PushDown(i);
int mid=(l+r)>>1;
if(pos<=mid) Modify(i<<1,l,mid,pos,val);
else Modify(i<<1|1,mid+1,r,pos,val);
PushUp(i);
}
int main()
{
n=rd();
for(int i=1;i<=n;i++)
a[i]=rd();
Build(1,1,n);
int Q=rd();
while(Q--)
{
int opt=rd();
if(opt==0)
{
int i=rd(),val=rd();
Modify(1,1,n,i,val);
}
else
{
int l=rd(),r=rd(),k=rd();
int res=0;
while(k--)
{
node ans=Query(1,1,n,l,r);
if(ans.mxs<=0) break;
res+=ans.mxs;
fx[++cnt]=make_pair(ans.l,ans.r);
Update(1,1,n,ans.l,ans.r);
}
printf("%d\n",res);
for(int i=cnt;i>=1;i--)
Update(1,1,n,fx[i].first,fx[i].second);
cnt=0;
}
}
return 0;
}
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com