码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)

时间:2017-09-20 12:13:30      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:mic   build   iostream   取反   最小   过程   maximum   最大的   sum   

【BZOJ3638】Cf172 k-Maximum Subsequence Sum

Description

给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少。1 ≤ n ≤ 105,1 ≤ m ≤ 105,1 ≤ l ≤ r ≤ n1 ≤ 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 线段树区间合并(模拟费用流)

标签:mic   build   iostream   取反   最小   过程   maximum   最大的   sum   

原文地址:http://www.cnblogs.com/CQzhangyu/p/7560373.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!