【BZOJ3638】Cf172 k-Maximum Subsequence Sum 線段樹區間合並(模擬費用流)
阿新 • • 發佈:2017-09-20
font uil sin upper sample dex else name etc
9 -8 9 -1 -1 -1 9 -8 9
3
1 1 9 1
1 1 9 2
1 4 6 3
25
0
【BZOJ3638】Cf172 k-Maximum Subsequence Sum
Description
給一列數,要求支持操作: 1.修改某個數的值 2.讀入l,r,k,詢問在[l,r]內選不相交的不超過k個子段,最大的和是多少。1 ≤ n ≤ 105,1 ≤ m ≤ 105,1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20
Sample Input
99 -8 9 -1 -1 -1 9 -8 9
3
1 1 9 1
1 1 9 2
1 4 6 3
Sample Output
1725
0
題解:如果直接用背包DP跑線段樹區間合並的話,復雜度是O(nk^2logn的
先考慮費用流的做法,我們第一次取的時候一定選擇的是區間中的最大連續子段,然後模擬費用流的過程,我們建立反向邊,即將原來的最大連續子段取反,然後再找最大連續子段。。。最後詢問完畢,再將哪些取反操作都還原回去。
所以一共要維護哪些東西呢?最大連續子段和以及它的位置,不過由於有取反操作,所以我們還要維護最小連續子段和以及它的位置,然後就沒了。。。
為了代碼美觀,區間合並的時候用了很多重載運算符,還是挺嚇人的~
#include <cstdio> #include <cstring> #include <iostream> #define lson x<<1 #define rson x<<1|1 const int maxn=100010; using namespace std; int n,m,ans; int v[maxn],pa[maxn],pb[maxn]; struct pp { int val,l,r; pp() {} pp(int x) {l=r=x,val=v[x];} pp(int a,int b,int c) {val=a,l=b,r=c;} pp operator + (const pp &a) const {return pp(val+a.val,l,a.r);} bool operator < (const pp a) const {return val<a.val;} }; struct node { bool tag; pp s,sm,sn,lm,ln,rm,rn; node() {} node(int x) { s=pp(x),tag=0; if(v[x]<0) ln=rn=sn=s,lm=sm=pp(0,x,x-1),rm=pp(0,x+1,x); else lm=rm=sm=s,ln=sn=pp(0,x,x-1),rn=pp(0,x+1,x); } }s[maxn<<2]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘)f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } inline int max(int a,int b,int c) {return max(max(a,b),c);} inline int min(int a,int b,int c) {return min(min(a,b),c);} inline pp max(const pp &a,const pp &b,const pp &c) {return max(max(a,b),c);} inline pp min(const pp &a,const pp &b,const pp &c) {return min(min(a,b),c);} inline node merge(const node &a,const node &b) { node c; c.lm=max(a.lm,a.s+b.lm),c.rm=max(b.rm,a.rm+b.s),c.sm=max(a.sm,b.sm,a.rm+b.lm); c.ln=min(a.ln,a.s+b.ln),c.rn=min(b.rn,a.rn+b.s),c.sn=min(a.sn,b.sn,a.rn+b.ln); c.s=a.s+b.s,c.tag=0; return c; } inline void rev(node &x) { x.sm.val=-x.sm.val,x.lm.val=-x.lm.val,x.rm.val=-x.rm.val; x.sn.val=-x.sn.val,x.ln.val=-x.ln.val,x.rn.val=-x.rn.val; x.s.val=-x.s.val,x.tag^=1; swap(x.sm,x.sn),swap(x.lm,x.ln),swap(x.rm,x.rn); } inline void pushdown(int x) { if(s[x].tag) rev(s[lson]),rev(s[rson]),s[x].tag=0; } void build(int l,int r,int x) { if(l==r) { s[x]=node(l); return ; } int mid=(l+r)>>1; build(l,mid,lson),build(mid+1,r,rson); s[x]=merge(s[lson],s[rson]); } void updata(int l,int r,int x,int a,int b) { if(a<=l&&r<=b) { rev(s[x]); return ; } pushdown(x); int mid=(l+r)>>1; if(a<=mid) updata(l,mid,lson,a,b); if(b>mid) updata(mid+1,r,rson,a,b); s[x]=merge(s[lson],s[rson]); } node query(int l,int r,int x,int a,int b) { if(a<=l&&r<=b) return s[x]; pushdown(x); int mid=(l+r)>>1; if(b<=mid) return query(l,mid,lson,a,b); if(a>mid) return query(mid+1,r,rson,a,b); return merge(query(l,mid,lson,a,b),query(mid+1,r,rson,a,b)); } void modify(int l,int r,int x,int a) { if(l==r) { s[x]=node(l); return ; } pushdown(x); int mid=(l+r)>>1; if(a<=mid) modify(l,mid,lson,a); else modify(mid+1,r,rson,a); s[x]=merge(s[lson],s[rson]); } int main() { n=rd(); int i,j,a,b,c; pp tmp; for(i=1;i<=n;i++) v[i]=rd(); build(1,n,1); m=rd(); for(i=1;i<=m;i++) { if(rd()==1) { a=rd(),b=rd(),c=rd(),ans=0; for(j=1;j<=c;j++) { tmp=query(1,n,1,a,b).sm,ans+=tmp.val; if(!tmp.val) break; pa[j]=tmp.l,pb[j]=tmp.r,updata(1,n,1,tmp.l,tmp.r); } for(j--;j;j--) updata(1,n,1,pa[j],pb[j]); printf("%d\n",ans); } else a=rd(),b=rd(),v[a]=b,modify(1,n,1,a); } return 0; }//9 9 -8 9 -1 -1 -1 9 -8 9 3 1 1 9 1 1 1 9 2 1 4 6 3
【BZOJ3638】Cf172 k-Maximum Subsequence Sum 線段樹區間合並(模擬費用流)