赤团开时斜飞去
题目描述
给定一个序列,有 \(q\) 次修改和询问。修改是区间 \([l,r]\) 加上 \(v\),询问时给定区间 \([l,r]\),问如果把这个区间划分成若干连续段,要最大化每一段的极差之和,求这个最大值。
\(n,q\le 2\times 10^5,|a_i|\le 10^9\)。
题解
我们把每个位置的值标到坐标系上第 \(i\) 个点的坐标为 \((a_i,i)\),然后连接相邻两个点。不难发现形成了若干折线,如下图。

注意到,对于一个点,如果它不是拐点,那么它可以直接和前一个点产生 \(|a_i-a_{i-1}|\) 的贡献。
于是我们就有一个 dp,设 \(f_i\) 表示前 \(i\) 个点,最后一段选的是 \(a_{i-1}\sim a_i\) 的线段。
转移显然:
-
不是拐点:\(f_i\leftarrow f_{i-1}+|a_i-a_{i-1}|\)。
-
是拐点:\(f_i\leftarrow \max(f_{i-1},f_{i-2}+|a_i-a_{i-1}|)\)。
这个东西我同学说可以广义矩乘 ddp 做,但是我不会。
我们考虑用线段树维护这个东西,一个节点维护这个节点管辖的区间的答案。维护一个 \(f_{2,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;
}

浙公网安备 33010602011771号