【BZOJ3638】Cf172 k-Maximum Subsequence 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 ≤ 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 
时间: 2024-10-13 00:34:30

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

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

题目描述 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少. 输入 The first line contains integer n (1 ≤ n ≤ 105), showing how many numbers the sequence has. The next line contains n integers a1, a2, ..., an (|ai| ≤ 500). The third line contain

HDU 5316 Magician(线段树区间合并入门)

本文纯属原创,转载请注明出处谢谢.http://blog.csdn.net/zip_fan. 题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5316 Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description Fantasy magicians usually gain their ability

hdu 3308(线段树区间合并)

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 6069    Accepted Submission(s): 2635 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index

HDU 3308 LCIS (线段树区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[mid + 1] > a[mid],写的细心点就好了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int MAXN = 1

poj3667 线段树 区间合并

1 //Accepted 3728 KB 1079 ms 2 //线段树 区间合并 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <queue> 7 #include <cmath> 8 #include <algorithm> 9 using namespace std; 10 /** 11 * This is a document

hdu3911 线段树 区间合并

1 //Accepted 3911 750MS 9872K 2 //线段树 区间合并 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <queue> 7 #include <cmath> 8 #include <algorithm> 9 using namespace std; 10 /** 11 * This is a documen

POJ 3667(线段树区间合并)

http://poj.org/problem?id=3667 题意:两个操作 : 1 选出靠左的长度为a的区间. 2 把从 a到a+b的区间清空. 线段树区间合并+lazy // by caonima // hehe #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <cmath> using namespace std; co

hdu4553约会安排(线段树区间合并)

链接 poj3667的加强版 当时的题解 这里只不过对于女神需要另开算,DS的占用的时间不加在女神身上,女神的时间都要加,清空的时候也都要算. 1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath>

CF 46 D Parking Lot(线段树区间合并)

Description Nowadays it is becoming increasingly difficult to park a car in cities successfully. Let's imagine a segment of a street as long as L meters along which a parking lot is located. Drivers should park their cars strictly parallel to the pav