赤团开时斜飞去

题目描述

给定一个序列,有 q 次修改和询问。修改是区间 [l,r] 加上 v,询问时给定区间 [l,r],问如果把这个区间划分成若干连续段,要最大化每一段的极差之和,求这个最大值。

n,q2×105,|ai|109

题解

我们把每个位置的值标到坐标系上第 i 个点的坐标为 (ai,i),然后连接相邻两个点。不难发现形成了若干折线,如下图。

image

注意到,对于一个点,如果它不是拐点,那么它可以直接和前一个点产生 |aiai1| 的贡献。

于是我们就有一个 dp,设 fi 表示前 i 个点,最后一段选的是 ai1ai 的线段。

转移显然:

  • 不是拐点:fifi1+|aiai1|

  • 是拐点:fimax(fi1,fi2+|aiai1|)

这个东西我同学说可以广义矩乘 ddp 做,但是我不会。

我们考虑用线段树维护这个东西,一个节点维护这个节点管辖的区间的答案。维护一个 f2,2 表示左边是否被选,右边是否被选(0 不选 1 选)的最大贡献和。

然后我们只需要考虑如何合并 2 个儿子,只需要考虑他们两个交接处是否形成了拐点。如果没有形成则两边都选,否则选一边(注意不选一定不优)。

修改就是单点改一下,这个和初始化差不多。

AC code:

#include<bits/stdc++.h>
#define int long long
#define N 200005
#define pii pair<int,int>
#define pcc pair<char,char>
#define x first
#define y second
#define pct __builtin_popcount
#define mod 1000000007
#define inf 2e18
#define pi acos(-1)
#define eps 1e-2
using namespace std;
int T=1,n,q,a[N],b[N];
bool check(int x,int y){
	return x>=0&&y>=0||x<=0&&y<=0;
}
struct sgt{
	struct node{
		int l,r,f[2][2];
		node operator+(const node &t)const{
			if(l==0)return t;
			int res[2][2]={
				0,0,
				0,0
			};
			for(int i=0;i<2;i++){
				for(int j=0;j<2;j++){
					if(check(b[r],b[r+1]))res[i][j]=f[i][1]+t.f[1][j];
					else res[i][j]=max(f[i][0]+t.f[1][j],f[i][1]+t.f[0][j]);
				}
			}
			return node({l,t.r,res[0][0],res[0][1],res[1][0],res[1][1]});
		}
	}tr[N<<2];
	void pushup(int u){
		tr[u]=tr[u<<1]+tr[u<<1|1];
	}
	void build(int u,int l,int r){
		tr[u].l=l;tr[u].r=r;
		if(l==r){
			tr[u].f[0][0]=0;
			tr[u].f[0][1]=0;
			tr[u].f[1][0]=0;
			tr[u].f[1][1]=abs(b[l]);
			return;
		}
		int mid=l+r>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
		pushup(u);
	}
	void modify(int u,int l,int r,int p){
		if(l==r){
			tr[u].f[0][0]=0;
			tr[u].f[0][1]=0;
			tr[u].f[1][0]=0;
			tr[u].f[1][1]=abs(b[l]);
			return;
		}
		int mid=l+r>>1;
		if(p<=mid)modify(u<<1,l,mid,p);
		else modify(u<<1|1,mid+1,r,p);
		pushup(u);
	}
	node qry(int u,int l,int r,int L,int R){
		if(l>=L&&r<=R)return tr[u];
		int mid=l+r>>1;
		node res={
			0,0,
			0,0,
			0,0
		};
		if(L<=mid)res=res+qry(u<<1,l,mid,L,R);
		if(R>mid)res=res+qry(u<<1|1,mid+1,r,L,R);
		return res;
	}
}tr;
void solve(int cs){
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(i>1)b[i-1]=a[i]-a[i-1];
	}
	tr.build(1,1,n-1);
	while(q--){
		int op,l,r,v;
		cin>>op;
		if(op==1){
			cin>>l>>r>>v;
			if(l>1){
				b[l-1]+=v;
				tr.modify(1,1,n-1,l-1);
			}
			if(r<n){
				b[r]-=v;
				tr.modify(1,1,n-1,r);
			}
		}
		else{
			cin>>l>>r;
			auto res=tr.qry(1,1,n-1,l,r-1);
			int mx=0;
			for(int i=0;i<2;i++){
				for(int j=0;j<2;j++){
					mx=max(mx,res.f[i][j]);
				}
			}
			cout<<mx<<'\n';
		}
	}
}
void solution(){
    /*
    nothing here
    */
}
signed main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//	init();
//	cin>>T;
    for(int cs=1;cs<=T;cs++){
        solve(cs);
    }
    return 0;
}
posted @   zxh923  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示