[JZOJ6223] 互膜 【线段树】【单调栈】【DP】

Description

在这里插入图片描述

Solution

我们可以设一个朴素的DP
f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示第 i − 1 i-1 i1轮上一个人是否操作了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=2n(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);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值