Description
Solution
我们可以设一个朴素的DP
记
f
[
i
]
[
0
/
1
]
f[i][0/1]
f[i][0/1]表示第
i
−
1
i-1
i−1轮上一个人是否操作了i这个位置,当前先手-当前后手的权值的最大值。
显然
A
n
s
=
S
u
m
+
f
[
1
]
[
0
]
Ans=Sum+f[1][0]
Ans=Sum+f[1][0]
容易得到转移
f
[
i
]
[
0
]
=
−
f
[
i
+
1
]
[
1
]
+
s
[
i
]
f[i][0]=-f[i+1][1]+s[i]
f[i][0]=−f[i+1][1]+s[i]
f
[
i
]
[
1
]
=
m
a
x
(
−
f
[
i
+
1
]
[
0
]
+
s
[
i
]
,
−
f
[
i
+
1
]
[
1
]
−
s
[
i
]
)
f[i][1]=max(-f[i+1][0]+s[i],-f[i+1][1]-s[i])
f[i][1]=max(−f[i+1][0]+s[i],−f[i+1][1]−s[i])
边界为
f
[
n
]
[
0
]
=
s
[
i
]
,
f
[
n
]
[
1
]
=
−
s
[
i
]
f[n][0]=s[i],f[n][1]=-s[i]
f[n][0]=s[i],f[n][1]=−s[i]
容易发现一个性质
f
[
i
]
[
0
]
≥
f
[
i
]
[
1
]
f[i][0]\geq f[i][1]
f[i][0]≥f[i][1]
且这两者的差似乎很有规律
记
d
[
i
]
=
f
[
i
]
[
0
]
−
f
[
i
]
[
1
]
d[i]=f[i][0]-f[i][1]
d[i]=f[i][0]−f[i][1]
代入上面的转移
f
[
i
]
[
0
]
=
−
f
[
i
+
1
]
[
0
]
+
d
[
i
+
1
]
+
s
[
i
]
f[i][0]=-f[i+1][0]+d[i+1]+s[i]
f[i][0]=−f[i+1][0]+d[i+1]+s[i]
f
[
i
]
[
1
]
=
m
a
x
(
−
f
[
i
+
1
]
[
0
]
+
s
[
i
]
,
−
f
[
i
+
1
]
[
0
]
+
d
[
i
+
1
]
−
s
[
i
]
)
f[i][1]=max(-f[i+1][0]+s[i],-f[i+1][0]+d[i+1]-s[i])
f[i][1]=max(−f[i+1][0]+s[i],−f[i+1][0]+d[i+1]−s[i])
=
m
a
x
(
s
[
i
]
,
d
[
i
+
1
]
−
s
[
i
]
)
−
f
[
i
+
1
]
[
0
]
=max(s[i],d[i+1]-s[i])-f[i+1][0]
=max(s[i],d[i+1]−s[i])−f[i+1][0]
上面减下面
d
[
i
]
=
d
[
i
+
1
]
+
s
[
i
]
−
m
a
x
(
s
[
i
]
,
d
[
i
+
1
]
−
s
[
i
]
)
d[i]=d[i+1]+s[i]-max(s[i],d[i+1]-s[i])
d[i]=d[i+1]+s[i]−max(s[i],d[i+1]−s[i])
分类讨论,若
d
[
i
+
1
]
−
s
[
i
]
≥
s
[
i
]
d[i+1]-s[i]\geq s[i]
d[i+1]−s[i]≥s[i]即
d
[
i
+
1
]
≥
2
s
[
i
]
d[i+1]\geq 2s[i]
d[i+1]≥2s[i],则
d
[
i
]
=
2
s
[
i
]
d[i]=2s[i]
d[i]=2s[i]
反之
d
[
i
]
=
d
[
i
+
1
]
d[i]=d[i+1]
d[i]=d[i+1]
容易看出
d
[
i
]
=
min
j
=
i
n
(
2
s
[
j
]
)
d[i]=\min\limits_{j=i}^{n}(2s[j])
d[i]=j=iminn(2s[j])
显然可以从前往后建一个递增的单调栈,用线段树维护单调栈就能够维护d了。
考虑计算答案。
f
[
i
]
[
0
]
=
−
f
[
i
+
1
]
[
0
]
+
d
[
i
+
1
]
+
s
[
i
]
f[i][0]=-f[i+1][0]+d[i+1]+s[i]
f[i][0]=−f[i+1][0]+d[i+1]+s[i]
一直推下去,可得
f
[
i
]
[
0
]
=
s
[
1
]
+
∑
i
=
2
n
(
−
1
)
i
(
d
[
i
]
−
s
[
i
]
)
f[i][0]=s[1]+\sum\limits_{i=2}^{n}(-1)^i\left(d[i]-s[i]\right)
f[i][0]=s[1]+i=2∑n(−1)i(d[i]−s[i])
d的带权和就用线段树维护单调栈来做,s的就直接算。
时间复杂度
O
(
n
log
2
n
)
O(n\log^2n)
O(nlog2n)
线段树维护单调栈的具体实现参考代码。
Code
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
const int N=200005;
typedef long long LL;
using namespace std;
LL sm[2*N],sp[2*N],mi[2*N],t[2*N][2];
int n,a[N],n1;
int tc(int x,int y)
{
if(((y-x)&1)||x>y) return 0;
else return (y&1)?-1:1;
}
int query(int k,int l,int r,int w,int v)
{
if(l==r)
{
if(mi[k]<v) return sm[k]+tc(l+1,w)*v;
else return tc(l,w)*v;
}
int mid=(l+r)>>1;
if(mi[t[k][1]]<=v) return sp[k]+query(t[k][1],mid+1,r,w,v);
else return query(t[k][0],l,mid,w,v);
}
void up(int k,int l,int r)
{
int mid=(l+r)>>1;
sp[k]=query(t[k][0],l,mid,mid,mi[t[k][1]]);
sm[k]=sm[t[k][1]]+sp[k];
mi[k]=min(mi[t[k][0]],mi[t[k][1]]);
}
void build(int k,int l,int r)
{
if(l==r) {mi[k]=2*a[l],sm[k]=(l&1)?-mi[k]:mi[k];return;}
int mid=(l+r)>>1;
build(t[k][0]=++n1,l,mid);
build(t[k][1]=++n1,mid+1,r);
up(k,l,r);
}
void modify(int k,int l,int r,int x,int v)
{
if(l==r) {mi[k]=v,sm[k]=(l&1)?-mi[k]:mi[k];return;}
int mid=(l+r)>>1;
if(x<=mid) modify(t[k][0],l,mid,x,v);
else modify(t[k][1],mid+1,r,x,v);
up(k,l,r);
}
int main()
{
cin>>n;
LL ans=0;
fo(i,1,n) scanf("%d",&a[i]),ans+=(i&1)?2*a[i]:0;
int q;
cin>>q;
n1=1;
build(1,2,n);
printf("%lld\n",(ans+sm[1])/2);
fo(t,1,q)
{
int x,y;
scanf("%d%d",&x,&y);
a[x]-=y;
ans-=(x&1)?2*y:0;
if(x!=1) modify(1,2,n,x,2*a[x]);
printf("%lld\n",(ans+sm[1])/2);
}
}