给出一个长度为n的序列,a1~an,和m次操作,每次操作分为
0 x val,将ax变成val
1 l r k,询问在区间l~r中,最多k个不重合区间的最大和是多少。
n,m<=10^5,|ai|<=500,1操作<=10000,|val|<=500,1<=k<=20
首先,可以肯定的是这道题一定是某种神奇的数据结构,不过好像很难维护。
咦?
如果询问区间一定,这东西真的不是网络流吗?!
把每个点i拆成i和i’,从i向i’连容量为1,费用为ai的边。从i’向i+1连容量为1,费用为0的边。从源点向点i连容量为1,费用为0的边,从每个点i’向汇点连容量为1,费用为0的边,那么每次增广的流量都为1,做最多k次就是答案了。(当增广费用小于0是便可退出)。
这道题就这样解决了!?
上面的方法一看就会爆炸。
发现每次增广的都一定是最大和子序列。
于是我们就可以用线段树来模拟网络流。
每一次找出最大和子序列,然后将其取反。
这道题就这样解决了。
注意实现。
%%%WerKeyTom_FTD
神奇的结构体套结构体黑科技
常数爆炸。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 100005
using namespace std;
struct doit{
int l,r,v;
friend doit operator +(doit y,doit z) {
doit x;x.v=y.v+z.v;x.l=y.l;x.r=z.r;return x;
}
friend bool operator <(doit y,doit z) {return y.v<z.v;}
};
struct note{
bool bz;
doit mx,mi,lmx,lmi,rmx,rmi,sum;
}t[N*3];
int n,x,l,r,k,m,ans,tot;
doit d[205];
note merge(note y,note z) {
note x;
x.sum=y.sum+z.sum;x.bz=0;
x.mx=max(y.mx,max(z.mx,y.rmx+z.lmx));
x.mi=min(y.mi,min(z.mi,y.rmi+z.lmi));
x.lmx=max(y.lmx,y.sum+z.lmx);
x.lmi=min(y.lmi,y.sum+z.lmi);
x.rmx=max(z.rmx,y.rmx+z.sum);
x.rmi=min(z.rmi,y.rmi+z.sum);
return x;
}
void back(int v) {
t[v].bz^=1;
swap(t[v].mx,t[v].mi);
swap(t[v].lmx,t[v].lmi);
swap(t[v].rmx,t[v].rmi);
t[v].sum.v*=-1;
t[v].mx.v*=-1;t[v].mi.v*=-1;
t[v].lmi.v*=-1;t[v].lmx.v*=-1;
t[v].rmi.v*=-1;t[v].rmx.v*=-1;
}
void down(int v) {
if (t[v].bz) t[v].bz=0,back(v*2),back(v*2+1);
}
void change(int v,int l,int r,int x,int y) {
if (l==r) {
t[v].bz=0;
t[v].sum.v=t[v].lmi.v=t[v].lmx.v=t[v].mi.v=
t[v].mx.v=t[v].rmi.v=t[v].rmx.v=y;
t[v].sum.l=t[v].lmi.l=t[v].lmx.l=t[v].mi.l=
t[v].mx.l=t[v].rmi.l=t[v].rmx.l=l;
t[v].sum.r=t[v].lmi.r=t[v].lmx.r=t[v].mi.r=
t[v].mx.r=t[v].rmi.r=t[v].rmx.r=l;
return;
}
int m=(l+r)/2;down(v);
if (x<=m) change(v*2,l,m,x,y);else change(v*2+1,m+1,r,x,y);
t[v]=merge(t[v*2],t[v*2+1]);
}
note find(int v,int l,int r,int x,int y) {
if (l==x&&r==y) return t[v];
int m=(l+r)/2;down(v);
if (y<=m) return find(v*2,l,m,x,y);
else if (x>m) return find(v*2+1,m+1,r,x,y);
else return merge(find(v*2,l,m,x,m),find(v*2+1,m+1,r,m+1,y));
}
void refuse(int v,int l,int r,int x,int y) {
if (l==x&&r==y) {back(v);return;}
int m=(l+r)/2;down(v);
if (y<=m) refuse(v*2,l,m,x,y);
else if (x>m) refuse(v*2+1,m+1,r,x,y);
else refuse(v*2,l,m,x,m),refuse(v*2+1,m+1,r,m+1,y);
t[v]=merge(t[v*2],t[v*2+1]);
}
int main() {
scanf("%d",&n);
fo(i,1,n) scanf("%d",&x),
change(1,1,n,i,x);
for(scanf("%d",&m);m;m--) {
scanf("%d",&x);
if (!x) {scanf("%d%d",&l,&r);change(1,1,n,l,r);continue;}
ans=0;
for(scanf("%d%d%d",&l,&r,&k);k;k--) {
note sum=find(1,1,n,l,r);if (sum.mx.v<0) break;
ans+=sum.mx.v;d[++tot]=sum.mx;
refuse(1,1,n,sum.mx.l,sum.mx.r);
}
printf("%d\n",ans);
for(;tot;tot--) refuse(1,1,n,d[tot].l,d[tot].r);
}
}
(话说CSDN的内置语言是神马?怎么c++变成了这幅鬼样!强迫症不爽)