给定一个序列,需要兹瓷两个操作:
1、修改一个元素的值
2、询问一个区间内选取不超过k个(不同询问的k不同但不会超过20)互不相交的子段和的最大值。
我们可以这样建图
把i拆成i和i’,然后i->i’一条容量为1(代表最多选一次)费用为ai的边。i’->i+1一条容量为1费用为0的边。s->i容量为1费用为0,i->t容量为1费用为0。那么我们现在就是要求总流量不超过k的最大费用流。做法是每一次找最长增广路,如果权值和小于0直接退出,否则加进答案并将沿途经过的所有边取反。
每一次最多增广1的流量,因此限定最多做k轮即可。
上面的网络流方法有很好的启发作用,我们并不需要真的跑网络流,而是直接模拟其算法过程,每一轮:
1:找到最大子段和,如果小于0就退出
2:加入答案,并将该段元素全部取相反数
因此我们只需要打一个能够维护区间最大(或小)(左或右或无)子段和的值(或左右端点)以及区间和。
为什么要维护最小值?因为区间取反后原来的最小值的相反数变成了取反后的最大值。
然后就上吧!
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
struct suan{
int l,r,s;
friend suan operator +(suan a,suan b){
suan c;
c.l=a.l;c.r=b.r;
c.s=a.s+b.s;
return c;
}
friend bool operator <(suan a,suan b){
return a.s<b.s;
}
};
struct dong{
suan smax,lmax,rmax,smin,lmin,rmin,all;
bool rev;
};
dong merge(dong a,dong b){
dong c;
c.smax=max(a.smax,b.smax);
c.smax=max(c.smax,a.rmax+b.lmax);
c.smin=min(a.smin,b.smin);
c.smin=min(c.smin,a.rmin+b.lmin);
c.lmax=max(a.lmax,a.all+b.lmax);
c.lmin=min(a.lmin,a.all+b.lmin);
c.rmax=max(b.rmax,a.rmax+b.all);
c.rmin=min(b.rmin,a.rmin+b.all);
c.all=a.all+b.all;
c.rev=0;
return c;
}
dong tree[maxn*5],sta[100],zlt;
int i,j,k,l,r,t,n,m,ans,top;
dong newnode(int x,int y){
dong c;
c.rev=0;
c.smax.s=c.smin.s=c.lmax.s=c.lmin.s=c.rmax.s=c.rmin.s=c.all.s=y;
c.smax.l=c.smin.l=c.lmax.l=c.lmin.l=c.rmax.l=c.rmin.l=c.all.l=x;
c.smax.r=c.smin.r=c.lmax.r=c.lmin.r=c.rmax.r=c.rmin.r=c.all.r=x;
return c;
}
void mark(int p){
tree[p].rev^=1;
swap(tree[p].smax,tree[p].smin);
swap(tree[p].lmax,tree[p].lmin);
swap(tree[p].rmax,tree[p].rmin);
tree[p].smax.s*=-1;
tree[p].smin.s*=-1;
tree[p].lmax.s*=-1;
tree[p].lmin.s*=-1;
tree[p].rmax.s*=-1;
tree[p].rmin.s*=-1;
tree[p].all.s*=-1;
}
void down(int p,int l,int r){
int mid=(l+r)/2;
if (tree[p].rev){
mark(p*2);
mark(p*2+1);
tree[p].rev=0;
}
}
void change(int p,int l,int r,int a,int b){
if (l==r){
tree[p]=newnode(l,b);
return;
}
down(p,l,r);
int mid=(l+r)/2;
if (a<=mid) change(p*2,l,mid,a,b);else change(p*2+1,mid+1,r,a,b);
tree[p]=merge(tree[p*2],tree[p*2+1]);
}
void reverse(int p,int l,int r,int a,int b){
if (l==a&&r==b){
mark(p);
return;
}
down(p,l,r);
int mid=(l+r)/2;
if (b<=mid) reverse(p*2,l,mid,a,b);
else if (a>mid) reverse(p*2+1,mid+1,r,a,b);
else reverse(p*2,l,mid,a,mid),reverse(p*2+1,mid+1,r,mid+1,b);
tree[p]=merge(tree[p*2],tree[p*2+1]);
}
dong query(int p,int l,int r,int a,int b){
if (l==a&&r==b) return tree[p];
down(p,l,r);
int mid=(l+r)/2;
if (b<=mid) return query(p*2,l,mid,a,b);
else if (a>mid) return query(p*2+1,mid+1,r,a,b);
else return merge(query(p*2,l,mid,a,mid),query(p*2+1,mid+1,r,mid+1,b));
}
int main(){
scanf("%d",&n);
fo(i,1,n){
scanf("%d",&t);
change(1,1,n,i,t);
}
scanf("%d",&m);
while (m--){
scanf("%d",&t);
if (!t){
scanf("%d%d",&j,&k);
change(1,1,n,j,k);
}
else{
scanf("%d%d%d",&l,&r,&k);
ans=top=0;
while (k--){
zlt=query(1,1,n,l,r);
if (zlt.smax.s<0) break;
ans+=zlt.smax.s;
sta[++top]=zlt;
reverse(1,1,n,zlt.smax.l,zlt.smax.r);
}
printf("%d\n",ans);
while (top){
reverse(1,1,n,sta[top].smax.l,sta[top].smax.r);
top--;
}
}
}
}