zoukankan      html  css  js  c++  java
  • BZOJ3638[Codeforces280D]k-Maximum Subsequence Sum&BZOJ3272Zgg吃东西&BZOJ3267KC采花——模拟费用流+线段树

    题目描述

    给一列数,要求支持操作: 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 contains integer m (1 ≤ m ≤ 105) — the number of queries. The next m lines contain the queries in the format, given in the statement.

    All changing queries fit into limits: 1 ≤ i ≤ n|val| ≤ 500.

    All queries to count the maximum sum of at most k non-intersecting subsegments fit into limits: 1 ≤ l ≤ r ≤ n1 ≤ k ≤ 20. It is guaranteed that the number of the queries to count the maximum sum of at most k non-intersecting subsegments doesn't exceed 10000.

    输出

    For each query to count the maximum sum of at most k non-intersecting subsegments print the reply — the maximum sum. Print the answers to the queries in the order, in which the queries follow in the input.

    样例输入

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

    样例输出

    17
    25
    0

    提示

    In the first query of the first example you can select a single pair (1, 9). So the described sum will be 17.

    Look at the second query of the first example. How to choose two subsegments? (1, 3) and (7, 9)? Definitely not, the sum we could get from (1, 3) and (7, 9) is 20, against the optimal configuration (1, 7) and (9, 9) with 25.

    The answer to the third query is 0, we prefer select nothing if all of the numbers in the given interval are negative.

    首先这个问题可以用费用流来解决:源点向每个点连边,容量为$1$,费用为$0$;第$i$个点向第$i+1$个点连边,容量为$1$,费用为$a_{i}$,每个点向汇点连边,容量为$1$,费用为$0$。可以知道每次增广一定是一段路径(即序列的最大连续子段和),然后将这段路径建反向边。那么我们可以模拟这个过程:每次取序列的最大连续子段和并将这一段的权值取反,假设第一次取了$[1,3]$而第二次取了$[3,5]$,那么相当于第一次取了$[1,2]$第二次取了$[4,5]$,中间的$3$在两次选取中权值抵消掉了。因为每次是贪心的取最大连续的一段,所以不会存在两次取的区间左端点或右端点相同,也就保证了每次取一个区间一定会增加一个子段。因为要取反,我们还要再维护最小连续子段和,然后在区间取反时将最大连续子段和与最小连续子段和调换一下即可。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,m;
    int opt;
    int l,r;
    int x,k;
    int ans;
    int rev[400010];
    struct miku
    {
    	int x,l,r;
    	miku(){};
    	miku(int X,int L,int R){x=X,l=L,r=R;}
    };
    queue<miku>q;
    struct Miku
    {
    	miku smx,smn,lmx,lmn,rmx,rmn,sum;
    }tr[400010];
    bool operator <(miku a,miku b){return a.x<b.x;}
    miku operator +(miku a,miku b){return miku(a.x+b.x,a.l,b.r);}
    inline Miku pushup(Miku ls,Miku rs)
    {
    	Miku rt;
    	rt.sum=ls.sum+rs.sum;
    	rt.smx=max(max(ls.smx,rs.smx),ls.rmx+rs.lmx);
    	rt.smn=min(min(ls.smn,rs.smn),ls.rmn+rs.lmn);
    	rt.lmx=max(ls.lmx,ls.sum+rs.lmx);
    	rt.lmn=min(ls.lmn,ls.sum+rs.lmn);
    	rt.rmx=max(rs.rmx,ls.rmx+rs.sum);
    	rt.rmn=min(rs.rmn,ls.rmn+rs.sum);
    	return rt;
    }
    inline void flip(int rt)
    {
    	swap(tr[rt].smx,tr[rt].smn);
    	swap(tr[rt].lmx,tr[rt].lmn);
    	swap(tr[rt].rmx,tr[rt].rmn);
    	rev[rt]^=1;
    	tr[rt].sum.x*=-1;
    	tr[rt].smx.x*=-1;
    	tr[rt].smn.x*=-1;
    	tr[rt].lmx.x*=-1;
    	tr[rt].lmn.x*=-1;
    	tr[rt].rmx.x*=-1;
    	tr[rt].rmn.x*=-1;
    }
    inline void pushdown(int rt)
    {
    	if(rev[rt])
    	{
    		rev[rt]^=1;
    		flip(rt<<1);
    		flip(rt<<1|1);
    	}
    }
    inline void build(int rt,int l,int r)
    {
    	if(l==r)
    	{
    		scanf("%d",&x);
    		tr[rt].sum=tr[rt].smx=tr[rt].smn=tr[rt].lmx=tr[rt].lmn=tr[rt].rmx=tr[rt].rmn=miku(x,l,r);
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(rt<<1,l,mid);
    	build(rt<<1|1,mid+1,r);
    	tr[rt]=pushup(tr[rt<<1],tr[rt<<1|1]);
    }
    inline void change(int rt,int l,int r,int k,int x)
    {
    	if(l==r)
    	{
    		tr[rt].sum=tr[rt].smx=tr[rt].smn=tr[rt].lmx=tr[rt].lmn=tr[rt].rmx=tr[rt].rmn=miku(x,l,r);
    		return ;
    	}
    	pushdown(rt);
    	int mid=(l+r)>>1;
    	if(k<=mid)
    	{
    		change(rt<<1,l,mid,k,x);
    	}
    	else
    	{
    		change(rt<<1|1,mid+1,r,k,x);
    	}
    	tr[rt]=pushup(tr[rt<<1],tr[rt<<1|1]);
    }
    inline void reverse(int rt,int l,int r,int L,int R)
    {
    	if(L<=l&&r<=R)
    	{
    		flip(rt);
    		return ;
    	}
    	pushdown(rt);
    	int mid=(l+r)>>1;
    	if(L<=mid)
    	{
    		reverse(rt<<1,l,mid,L,R);
    	}
    	if(R>mid)
    	{
    		reverse(rt<<1|1,mid+1,r,L,R);
    	}
    	tr[rt]=pushup(tr[rt<<1],tr[rt<<1|1]);
    }
    inline Miku query(int rt,int l,int r,int L,int R)
    {
    	if(L<=l&&r<=R)
    	{
    		return tr[rt];
    	}
    	pushdown(rt);
    	int mid=(l+r)>>1;
    	if(R<=mid)
    	{
    		return query(rt<<1,l,mid,L,R);
    	}
    	else if(L>mid)
    	{
    		return query(rt<<1|1,mid+1,r,L,R);
    	}
    	else
    	{
    		return pushup(query(rt<<1,l,mid,L,R),query(rt<<1|1,mid+1,r,L,R));
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	build(1,1,n);
    	scanf("%d",&m);
    	while(m--)
    	{
    		scanf("%d",&opt);
    		if(!opt)
    		{
    			scanf("%d%d",&k,&x);
    			change(1,1,n,k,x);
    		}
    		else
    		{
    			scanf("%d%d%d",&l,&r,&k);
    			ans=0;
    			while(k--)
    			{
    				Miku res=query(1,1,n,l,r);
    				if(res.smx.x<0)
    				{
    					break;
    				}
    				ans+=res.smx.x;
    				q.push(res.smx);
    				reverse(1,1,n,res.smx.l,res.smx.r);
    			}
    			while(!q.empty())
    			{
    				miku res=q.front();
    				q.pop();
    				reverse(1,1,n,res.l,res.r);
    			}
    			printf("%d
    ",ans);
    		}
    	}
    }
  • 相关阅读:
    兄弟连,一般人来不起,来的肯定不是一般人!
    50天之脱变,66期第一个项目感受。切记平常心
    2016十大影响事件
    为什么要写年终总结
    20161228阅读笔记
    为什么要认识牛人
    李笑来:演讲能力是我这一生有幸学到的最重要能力
    张辉:工作几年就应该给自己“清零”
    小马宋:目标决定方法~坚持目标而不是方法
    李笑来的财务自由法~把一份时间卖出很多份
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10426613.html
Copyright © 2011-2022 走看看