CF280D k-Maximum Subsequence Sum
(题目传送门)
题意:一个长度为
的序列,要求支持下列操作
- 单点修改某个数的值
- 求区间
选出至多 个不相交的子段和的最大值
模拟费用流/反悔贪心的典题
从费用流的角度考虑。从源点
考虑増广的过程,找到一条费用最大的边进行増广,并建对应的反向弧。其实就相当于选出一个最大子段和的区间,并将它们取相反数(费用流反向弧的费用等于正向弧相反数)
因此,我们就可以使用模拟费用流来解决这个问题
从贪心的角度思考,每次都选择最大子段和,但选中的数可能有些不是最优解,因此我们的选择要能支持反悔。具体操作就是将选过的最大子段变成相反数,这样下次选中它们时就相当于反悔操作,不选这些数。这样操作就一定能取到最优解
现在考虑如何实现模拟费用流/贪心。它需要我们能支持查询区间最大子段和,取反最大子段,单点修改某个值。很容易想到线段树。因为有取反操作,所以还需要维护最小子段和,同时还需要维护最大/最小子段所代表的区间才能进行取反操作,写一个结构体会好写很多
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;
const LL INF=1e18;
int n,q,a[N];
LL ans;
queue < pair<int,int> > qq;
struct node{int l,r;LL s;}; node zinf={0,0,INF},finf={0,0,-INF};
node operator + (const node &a,const node &b){return {a.l,b.r,a.s+b.s};}
bool operator < (const node &a,const node &b){return a.s<b.s;}
#define lc(p) p<<1
#define rc(p) p<<1|1
struct Seg
{
node mx,mn,lmx,rmx,lmn,rmn,sum;
int rev;
#define rev(x) tree[x].rev
void write(int l,int r,LL s)
{
mx=mn=lmx=rmx=lmn=rmn=sum={l,r,s};
rev=0;
}
void reverse()
{
swap(mx,mn); swap(lmx,lmn); swap(rmx,rmn);
mx.s*=-1; mn.s*=-1;
lmx.s*=-1; rmx.s*=-1;
lmn.s*=-1; rmn.s*=-1;
sum.s*=-1; rev^=1;
}
}tree[N<<2];
Seg I={finf,zinf,finf,finf,zinf,zinf,{0,0,0},0};
Seg pushup(Seg p,Seg q)
{
Seg val=I;
if(p.mx.s>=1e16)
return q;
if(q.mx.s>=1e16)
return p;
val.mx=max(max(p.mx,q.mx),p.rmx+q.lmx);
val.mn=min(min(p.mn,q.mn),p.rmn+q.lmn);
val.lmx=max(p.lmx,p.sum+q.lmx);
val.rmx=max(q.rmx,p.rmx+q.sum);
val.lmn=min(p.lmn,p.sum+q.lmn);
val.rmn=min(q.rmn,p.rmn+q.sum);
val.sum=p.sum+q.sum;
return val;
}
void spread(int p)
{
if(rev(p))
{
tree[lc(p)].reverse();
tree[rc(p)].reverse();
rev(p)=0;
}
}
void build(int p,int l,int r)
{
if(l==r)
{
tree[p].write(l,r,a[l]);
return;
}
int mid=(l+r)>>1;
build(lc(p),l,mid);
build(rc(p),mid+1,r);
tree[p]=pushup(tree[lc(p)],tree[rc(p)]);
}
void change(int p,int l,int r,int x,int v)
{
if(l==r)
{
tree[p].write(l,r,v);
return;
}
spread(p);
int mid=(l+r)>>1;
if(x<=mid)
change(lc(p),l,mid,x,v);
else
change(rc(p),mid+1,r,x,v);
tree[p]=pushup(tree[lc(p)],tree[rc(p)]);
}
void reverse(int p,int l,int r,int ql,int qr)
{
if(ql<=l && qr>=r)
return tree[p].reverse(),void();
spread(p);
int mid=(l+r)>>1;
if(ql<=mid)
reverse(lc(p),l,mid,ql,qr);
if(qr>mid)
reverse(rc(p),mid+1,r,ql,qr);
tree[p]=pushup(tree[lc(p)],tree[rc(p)]);
}
Seg ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l && qr>=r)
return tree[p];
spread(p);
int mid=(l+r)>>1;
Seg val=I,lval=I,rval=I;
if(ql<=mid)
lval=ask(lc(p),l,mid,ql,qr);
if(qr>mid)
rval=ask(rc(p),mid+1,r,ql,qr);
val=pushup(lval,rval);
return val;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&q);
while(q--)
{
int op,l,r,k,x,v;
scanf("%d",&op);
if(op==0)
{
scanf("%d%d",&x,&v);
change(1,1,n,x,v);
}
else
{
scanf("%d%d%d",&l,&r,&k);
ans=0;
while(k--)
{
Seg cur=ask(1,1,n,l,r);
if(cur.mx.s<=0)
break;
ans+=cur.mx.s;
reverse(1,1,n,cur.mx.l,cur.mx.r);
qq.push({cur.mx.l,cur.mx.r});
}
printf("%lld\n",ans);
while(qq.size())
{
reverse(1,1,n,qq.front().first,qq.front().second);
qq.pop();
}
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?