题目大意:给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少。
如果仅仅是询问一次,我们可以构图费用流,跑一次就行了。但题目是多组询问,直接费用流显然会TLE。自然地,我们想到对残余网络进行增广,但每次地询问区间不同,而且有修改操作,这种做法也不可行。本蒟蒻膜拜了一下hzwer大大的题解,发现根据费用流每次求最长路的性质,可以手动增广,也就是每次用线段树求一个最大子段和,然后将整个区间取反,继续增广,直到和为0或者个数达到K次为止。(可以这么理解,增广之后正向弧达到满流,反向弧流减少,而反向弧中费用为负值)接下来就是线段树的基本操作了。我们要维护区间最大和以及位置(正向弧和反向弧的信息都要维护),还要维护一个flag记录上次增广的是哪条弧,但只要想清楚还是很好写的。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=200000+10;
struct node
{
int lx,lp,rx,rp,sum;
int mx,p1,p2;
};
struct data
{
node mx,mn;
int flag;
}t[maxn*4];
struct arr
{
int l,r;
};
vector<arr> h;
int n,m,op,a[maxn],cc;
void init(node &p,int l,int v)
{
p.lp=p.rp=p.p1=p.p2=l;
p.mx=p.lx=p.rx=p.sum=v;
}
node merge(node a,node b)
{
node res;res.sum=a.sum+b.sum;
res.lx=a.lx;res.lp=a.lp;
if(a.sum+b.lx>res.lx) res.lx=a.sum+b.lx,res.lp=b.lp;
res.rx=b.rx;res.rp=b.rp;
if(b.sum+a.rx>res.rx) res.rx=b.sum+a.rx,res.rp=a.rp;
res.mx=a.mx;res.p1=a.p1;res.p2=a.p2;
if(b.mx>res.mx) res.mx=b.mx,res.p2=b.p2,res.p1=b.p1;
if(a.rx+b.lx>res.mx) res.mx=a.rx+b.lx,res.p1=a.rp,res.p2=b.lp;
return res;
}
void pushup(int p)
{
t[p].mx=merge(t[p*2].mx,t[p*2+1].mx);
t[p].mn=merge(t[p*2].mn,t[p*2+1].mn);
}
void pushdown(int p)
{
if(t[p].flag)
{
swap(t[p*2].mn,t[p*2].mx);
swap(t[p*2+1].mn,t[p*2+1].mx);
t[p].flag^=1;t[p*2].flag^=1;t[p*2+1].flag^=1;
}
}
void build(int p,int l,int r)
{
if(l==r)
{
init(t[p].mx,l,a[l]);
init(t[p].mn,l,-a[l]);
return;
}
int mid=(l+r)>>1;
build(p*2,l,mid);build(p*2+1,mid+1,r);
pushup(p);
}
void update(int p,int l,int r,int x,int v)
{
if(x<l||x>r) return;
if(l==r)
{
init(t[p].mx,l,v);
init(t[p].mn,l,-v);
return;
}
pushdown(p);
int mid=(l+r)>>1;
update(p*2,l,mid,x,v);
update(p*2+1,mid+1,r,x,v);
pushup(p);
}
void reverse(int p,int l,int r,int x,int y)
{
if(l>y||r<x) return;
if(l>=x&&r<=y)
{
swap(t[p].mn,t[p].mx);
t[p].flag^=1;
return;
}
pushdown(p);int mid=(l+r)>>1;
reverse(p*2,l,mid,x,y);
reverse(p*2+1,mid+1,r,x,y);
pushup(p);
}
node query(int p,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
{
return t[p].mx;
}
int mid=(l+r)>>1;
pushdown(p);
if(y<=mid) return query(p*2,l,mid,x,y);
else if(x>mid) return query(p*2+1,mid+1,r,x,y);
else return merge(query(p*2,l,mid,x,y),query(p*2+1,mid+1,r,x,y));
}
void solve(int l,int r,int k)
{
int res=0;h.clear();
for(int i=1;i<=k;i++)
{
node hm=query(1,1,n,l,r);
if(hm.mx<=0) break;
else res+=hm.mx;
reverse(1,1,n,hm.p1,hm.p2);
h.push_back((arr){hm.p1,hm.p2});
//cout<<hm.p1<<' '<<hm.p2<<endl;
}
for(int i=h.size()-1;i>=0;i--)
reverse(1,1,n,h[i].l,h[i].r);
printf("%d\n",res);
}
int main()
{
//freopen("3638.in","r",stdin);
//freopen("3638.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);int op,l,r,k;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
cc++;
scanf("%d%d%d",&op,&l,&r);
if(op!=1) update(1,1,n,l,r),a[l]=r;
else
{
scanf("%d",&k);
solve(l,r,k);
}
}
return 0;
}