1. 程式人生 > >【BZOJ3638】Cf172 k-Maximum Subsequence Sum 線段樹區間合並(模擬費用流)

【BZOJ3638】Cf172 k-Maximum Subsequence Sum 線段樹區間合並(模擬費用流)

font uil sin upper sample dex else name etc

【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

9
9 -8 9 -1 -1 -1 9 -8 9
3
1 1 9 1
1 1 9 2
1 4 6 3

Sample Output

17
25
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 線段樹區間合並(模擬費用流)