zoukankan      html  css  js  c++  java
  • CF280D k-Maximum Subsequence Sum

    $$color{#66ccff}{ exttt{->原题传送门<-}}$$

    【题目描述】

    长度为(n)(n le 10^5))的数列(a_i),进行(m)次操作((m le 10^5))支持两种操作:

    • (0 ; i ; val :)(a_i)修改为(val)

    • (1 ; l ; r ; k:) 询问区间([l,r])里选出至多(k)个不相交的子段和的最大值。((sum_{}k le 10^5)

    【题解】

    先考虑几个简单的问题:

    1. (k=1),则该问题就是(GSS3)

    2. 若没有修改操作,并且操作(2)(l=1、r=n),则是一个经典的最大(k)段子段和问题。

    问题(1)不用多说,用线段树维护(Lmax、Rmax、Sum、Ans)即可。

    对于问题(2),考虑贪心,取(k)次,每次都选择最大子段和。

    然而,一个数最多只能被选一次,所以每选一次后要删除它对后续操作的影响。

    容易发现一个结论:我们每次把所选区间内的数进行取反操作即可删除影响(即选不到这个数)。

    这一点可以根据费用流的思想取证明,这里就不再赘述。

    现在我们考虑如何高效的模拟这个过程。

    首先,这个过程能与问题(1)的线段树解法相结合呢?

    答案是肯定的。

    我们发现,不管多少次取负,一个位置的数只有两种可能(一正一负),所以我们可以维护两颗线段树。

    合并时分别合并,取负时找到对应区间,交换两者信息 并打上标记即可。

    再结合问题(1)的单点修改,这个问题就可以得到完美解决。

    (Code:)(内有部分良心注释)

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<stack>
    using namespace std;
    #define ll long long
    #define rg register
    struct ios{
    	template<typename TP>
    	inline ios operator >> (TP &x)
    	{
    		TP f=1;x=0;rg char c=getchar();
    		for(;c>'9' || c<'0';c=getchar()) if(c=='-') f=-1;
    		for(;c>='0' && c<='9';c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    		x*=f;
    		return *this;
    	}
    	template<typename TP>
    	inline ios operator << (TP x)
    	{
    		int top=0,s[66];
    		if(x<0) x=-x,putchar('-');
    		if(!x) putchar('0');
    		while(x) s[++top]=x%10+'0',x/=10;
    		while(top) putchar(s[top--]);
    		return *this;
    	}
    	inline ios operator << (char s)
    	{
    		putchar(s);
    		return *this;
    	}
    }io;
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson l,mid,ls
    #define rson mid+1,r,rs
    const int N=1e5+5;
    int n,m,a[N];
    bool Tag[N<<2];
    struct node{
    	int Lmax,Rmax,Ans,Sum;
            /*
                    Lmax:区间从左端开始的最大子段和
                    Rmax:区间从右端开始的最大子段和
                    Sum:区间和
                    Ans:区间最大子段和
            */
    	int L,R,Ansl,Ansr;
    	/*
    		L:区间左端最长子段的右端点
    		R:区间右端最长子段的左端点
    		↓↓↓↓↓↓↓↓↓↓维护↓↓↓↓↓↓↓↓↓↓
    		Ansl:当前区间的最长子段的左端点
    		Ansr:当前区间的最长子段的右端点
    
    		建一正一负两颗线段树
    		但一段区间在两种情况下的最长子段和的值和对应的位置不一定相同
    		所以要维护Ansl、Ansr
    		维护Ansl、Ansr就要用到L、R
    	*/
    	inline node(rg int pos=0,rg int val=0) {L=R=Ansl=Ansr=pos,Lmax=Rmax=Ans=Sum=val;}
    }t[2][N<<2];
    stack<node>q;
    inline node operator + (node A,node B)
    {
    	node Ans;
    	if(A.Lmax>A.Sum+B.Lmax) Ans.Lmax=A.Lmax,Ans.L=A.L;
    	else Ans.Lmax=A.Sum+B.Lmax,Ans.L=B.L;
            //容易发现一个区间的Lmax只可能是左儿子的Lmax或右儿子的Lmax+左儿子的区间和
    
    	if(B.Rmax>A.Rmax+B.Sum) Ans.Rmax=B.Rmax,Ans.R=B.R;
    	else Ans.Rmax=B.Sum+A.Rmax,Ans.R=A.R;
            //同上
    
    	Ans.Sum=A.Sum+B.Sum;
    	if(A.Ans>B.Ans) Ans.Ans=A.Ans,Ans.Ansl=A.Ansl,Ans.Ansr=A.Ansr;
    	else Ans.Ans=B.Ans,Ans.Ansl=B.Ansl,Ans.Ansr=B.Ansr;
    	if(A.Rmax+B.Lmax>Ans.Ans) Ans.Ans=A.Rmax+B.Lmax,Ans.Ansl=A.R,Ans.Ansr=B.L;
    	return Ans;
    }
    inline void pushup(int rt)
    {
    	t[0][rt]=t[0][ls]+t[0][rs];
    	t[1][rt]=t[1][ls]+t[1][rs];
    }
    inline void build(int l,int r,int rt)
    {
    	// io<<l<<' '<<r<<' '<<rt<<'
    ';
    	if(l==r)
    	{
    		t[0][rt]=node(l,a[l]);
    		t[1][rt]=node(l,-a[l]);
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(lson),build(rson);
    	pushup(rt);
    }
    inline void Get(int rt)
    {
    	swap(t[0][rt],t[1][rt]);
    	Tag[rt]^=1;
    }
    inline void update(int p,int val,int l,int r,int rt)
    {
    	// io<<l<<' '<<r<<' '<<rt<<'
    ';
    	if(l==r)
    	{
    		t[0][rt]=node(l,val);
    		t[1][rt]=node(l,-val);
    		return;
    	}
    	if(Tag[rt])
    	{
    		Get(ls),Get(rs);
    		Tag[rt]=0;
    	}
    	int mid=(l+r)>>1;
    	if(p<=mid) update(p,val,lson);
    	else update(p,val,rson);
    	pushup(rt);
    }
    inline void Modify(int L,int R,int l,int r,int rt)//区间取反
    {
    	if(L<=l && r<=R)
    	{
    		Get(rt);
    		return;
    	}
    	if(Tag[rt])
    	{
    		Get(ls),Get(rs);
    		Tag[rt]=0;
    	}
    	int mid=(l+r)>>1;
    	if(L<=mid) Modify(L,R,lson);
    	if(R>mid) Modify(L,R,rson);
    	pushup(rt);
    }
    inline node query(int L,int R,int l,int r,int rt)//查询可以覆盖区间[L,R]的节点的信息
    {
    	if(L<=l && r<=R) return t[0][rt];
    	if(Tag[rt])
    	{
    		Get(ls),Get(rs);
    		Tag[rt]=0;
    	}
    	int mid=(l+r)>>1;
    	if(R<=mid) return query(L,R,lson);
    	else if(L>mid) return query(L,R,rson);
    	else return query(L,mid,lson)+query(mid+1,R,rson);
    }
    int main()
    {
    	// freopen("CF280D.in","r",stdin);
    	io>>n;
    	for(rg int i=1;i<=n;++i) io>>a[i];
    	build(1,n,1),io>>m;
    	for(rg int _=1,op,l,r,k;_<=m;++_)
    	{
    		io>>op;
    		if(!op)
    		{
    			io>>l>>r;
    			update(l,r,1,n,1);
    		}
    		else
    		{
    			io>>l>>r>>k;int ans=0;
    			for(rg int i=1;i<=k;++i)
    			{
    				node Ans=query(l,r,1,n,1);
    				if(Ans.Ans<=0) break;
    				// io<<Ans.Ans<<' ';
    				ans+=Ans.Ans,q.push(Ans);//记录被影响的区间,查询完成后再撤销
    				Modify(Ans.Ansl,Ans.Ansr,1,n,1);
    			}
    			// io<<'
    ';
    			io<<ans<<'
    ';
    			while(!q.empty())
    			{
    				node t=q.top();q.pop();
    				Modify(t.Ansl,t.Ansr,1,n,1);
    			}
    		}
    	}
    	return 0;
    }
    /*
    15
    -4 8 -3 -10 10 4 -7 -7 0 -6 3 8 -10 7 2
    15
    1 3 9 2
    0 5 -10
    1 3 9 2
    */
    /*
    15
    -4 8 -3 -10 10 4 -7 -7 0 -6 3 8 -10 7 2
    15
    1 3 9 2
    1 6 12 1
    0 6 5
    0 10 -7
    1 4 9 1
    1 7 9 1
    0 10 -3
    1 4 10 2
    1 3 13 2
    1 4 11 2
    0 15 -9
    0 13 -9
    0 11 -10
    1 5 14 2
    1 6 12 1
    */
    /*
    20
    -5 -1 -9 -6 4 -5 6 1 5 -3 6 -3 10 1 4 -10 -10 -9 10 -6
    20
    0 19 0
    1 1 14 3
    1 8 20 4
    1 9 11 1
    1 17 19 1
    0 13 4
    0 9 7
    1 2 11 2
    0 20 -8
    1 8 19 1
    0 2 3
    1 6 11 1
    0 6 -10
    1 8 13 1
    1 9 15 1
    0 17 -8
    1 3 13 1
    1 1 14 4
    0 17 0
    1 7 19 1
    */
    
    
  • 相关阅读:
    MYSQL存储引擎
    微信公众号自定义菜单
    TCP 三次握手与四次挥手
    微信扫码关注公众号并登录网站
    redis scan命令使用
    [转]Maven多模块结构下版本管理的正确姿势-revision
    线程间的协作wait,notify,sleep,yield,join
    GIT 撤销操作
    Kafka学习理解-listeners配置
    Kafka 简介梳理
  • 原文地址:https://www.cnblogs.com/p-z-y/p/11741028.html
Copyright © 2011-2022 走看看