题链:
http://codeforces.com/problemset/problem/280/D
题解:
神题,巨恶心。
(把原来的那个dp题升级为:序列带修 + 多次询问区间[l,r]内取不超过k段的不重叠子串,使得其和最大)。
按费用流的思路来看,建图方法如下:
每个点拆成两个点 i , i' ,建立超源 S和超汇 T
i -> i' : (1,a[i])
S -> i : (1,0)
i'-> T : (1,0)
i'-> i+1: (1,0)
那么对于某段区间,按照spfa最长路费用流去一条路一条路增广,
直到某个时候增广数==k或者增广路的费用为负数就停止。
分析其增广方式,不难发现一个重要特点(可以自己简单伪证一下哈):
每次找到增广路都是连续的一段,即对应着序列区间上和最大的连续的一段。
接下来增广操作,就会取出这一段的权值和,并把这一段的所有数全部 * -1。(就是增广后的反向边的花费)
所以就用线段树维护区间最大子段和以及最小子段和,
并且要支持单点修改和区间 * -1操作。
每次就取出[l,r]区间内的最大的子段,并把对应的子段全部 * -1,
如果取得次数==k或者最大的子段和为负数就停止。
。。。这个线段树不是一般的烦。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 100005 #define INF 0x3f3f3f3f using namespace std; int a[MAXN]; struct data{ int sval,lval,rval,sum,sl,sr,lr,rl; void init(bool type){ if(type) lval=rval=sval=-INF; else lval=rval=sval=+INF; } void reverse(){ sval*=-1; lval*=-1; rval*=-1; sum*=-1; } void update(const data &l,const data &r,bool type){ sum=l.sum+r.sum; if(type) lval=max(l.lval,l.sum+r.lval), rval=max(r.rval,r.sum+l.rval), sval=max(max(l.sval,r.sval),l.rval+r.lval); else lval=min(l.lval,l.sum+r.lval), rval=min(r.rval,r.sum+l.rval), sval=min(min(l.sval,r.sval),l.rval+r.lval); if(l.lval==lval) lr=l.lr; else lr=r.lr; if(r.rval==rval) rl=r.rl; else rl=l.rl; if(l.sval==sval) sl=l.sl,sr=l.sr; else if(r.sval==sval) sl=r.sl,sr=r.sr; else sl=l.rl,sr=r.lr; } }; struct info{ data maxi,mini; void init(){ maxi.init(1); mini.init(0); } }; struct SGT{ #define ls lson[u] #define rs rson[u] bool lazy[MAXN<<1]; int lson[MAXN<<1],rson[MAXN<<1],rt,sz; info node[MAXN<<1]; void init(){ rt=sz=0; memset(lazy,0,sizeof(lazy)); memset(lson,0,sizeof(lson)); memset(rson,0,sizeof(rson)); for(int i=0;i<(MAXN<<1);i++) node[i].init(); } void pushup(info &now,const info &l,const info &r){ now.maxi.update(l.maxi,r.maxi,1);//___________________维护最大连续和_1__ now.mini.update(l.mini,r.mini,0);//___________________维护最小连续和_0__ } void pushdown(int u){ node[ls].maxi.reverse(); node[ls].mini.reverse(); node[rs].maxi.reverse(); node[rs].mini.reverse(); swap(node[ls].maxi,node[ls].mini); swap(node[rs].maxi,node[rs].mini); lazy[u]^=1; lazy[ls]^=1; lazy[rs]^=1; } void build(int &u,int l,int r){ u=++sz; if(l==r) { node[u].maxi=(data){a[l],a[l],a[l],a[l],l,r,r,l}; node[u].mini=(data){a[l],a[l],a[l],a[l],l,r,r,l}; return; } int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); pushup(node[u],node[ls],node[rs]); } void modify(int u,int l,int r,int p){ if(l==r){ node[u].maxi=(data){a[l],a[l],a[l],a[l],l,r,r,l}; node[u].mini=(data){a[l],a[l],a[l],a[l],l,r,r,l}; return; } if(lazy[u]) pushdown(u); int mid=(l+r)>>1; if(p<=mid) modify(ls,l,mid,p); else modify(rs,mid+1,r,p); pushup(node[u],node[ls],node[rs]); } void modify(int u,int l,int r,int al,int ar){ if(al<=l&&r<=ar){ node[u].maxi.reverse(); node[u].mini.reverse(); swap(node[u].maxi,node[u].mini); lazy[u]^=1; return; } if(lazy[u]) pushdown(u); int mid=(l+r)>>1; if(al<=mid) modify(ls,l,mid,al,ar); if(mid<ar) modify(rs,mid+1,r,al,ar); pushup(node[u],node[ls],node[rs]); } info query(int u,int l,int r,int al,int ar){ if(al<=l&&r<=ar) return node[u]; info now,lnode,rnode; now.init(); lnode.init(); rnode.init(); if(lazy[u]) pushdown(u); int mid=(l+r)>>1; if(al<=mid) lnode=query(ls,l,mid,al,ar); if(mid<ar) rnode=query(rs,mid+1,r,al,ar); if(mid<al) now=rnode; else if(ar<=mid) now=lnode; else pushup(now,lnode,rnode); return now; } #undef ls #undef rs }T1; int N,M,ans; void dfs(int k,int l,int r){ info now=T1.query(T1.rt,1,N,l,r); if(now.maxi.sval<=0) return; ans+=now.maxi.sval; T1.modify(T1.rt,1,N,now.maxi.sl,now.maxi.sr); if(k-1)dfs(k-1,l,r); T1.modify(T1.rt,1,N,now.maxi.sl,now.maxi.sr); } int main() { //freopen("280D.in","r",stdin); scanf("%d",&N); T1.init(); for(int i=1;i<=N;i++) scanf("%d",&a[i]); T1.build(T1.rt,1,N); scanf("%d",&M); for(int i=1,c,l,r,k;i<=M;i++){ scanf("%d",&c); if(!c){ scanf("%d",&k); scanf("%d",&a[k]); T1.modify(T1.rt,1,N,k); } else{ scanf("%d%d%d",&l,&r,&k); ans=0; dfs(k,l,r); printf("%d ",ans); } } return 0; }