[JZOJ6223] 互膜 【線段樹】【單調棧】【DP】

Description

在這裏插入圖片描述

Solution

我們可以設一個樸素的DP
f[i][0/1]f[i][0/1]表示第i1i-1輪上一個人是否操作了i這個位置,當前先手-當前後手的權值的最大值。
顯然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][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[i][0]f[i][1]f[i][0]\geq 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][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]) =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]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]2s[i]d[i+1]\geq 2s[i],則d[i]=2s[i]d[i]=2s[i]
反之d[i]=d[i+1]d[i]=d[i+1]
容易看出d[i]=minj=in(2s[j])d[i]=\min\limits_{j=i}^{n}(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]=s[1]+i=2n(1)i(d[i]s[i])f[i][0]=s[1]+\sum\limits_{i=2}^{n}(-1)^i\left(d[i]-s[i]\right)

d的帶權和就用線段樹維護單調棧來做,s的就直接算。
時間複雜度O(nlog2n)O(n\log^2n)

線段樹維護單調棧的具體實現參考代碼。

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);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章