洛谷P5281 [ZJOI2019] Minimax搜索

設修改前根節點的權值爲 \(W\),不難發現,必定存在一條從根到葉子的鏈,鏈上的每一個點權值都爲 \(W\),那麼只要這條鏈上任意一點權值改變,根節點權值最後必定會改變。c++

考慮一個葉子 \(i\),且 \(w_i \neq W\),要想經過改變該點權值來讓鏈上的點發生改變,該點必須知足如下一個狀況:git

\(w_i<W\),且該點祖先和鏈的第一個交點的深度爲奇數,這時將其改成 \(W+1\)優化

\(w_i > W\),且該點祖先和鏈的第一個交點的深度爲偶數,這時將其改成 \(W-1\)spa

直接處理代價爲 \(k\) 的狀況很差處理,考慮求出代價 \(\leqslant k\) 的方案後差分。code

將問題轉化爲機率來考慮,鏈上的點設 \(f_x\) 表示 \(x\) 子樹內的葉子經過代價 \(\leqslant k\) 的修改後 \(x\) 權值不變的機率,得最終方案數爲總方案乘 \(1-\prod f_x\)。考慮不在鏈上的點,若其深度爲奇數,\(DP\) 值爲權值 \(<W\) 的機率,若其深度爲偶數,\(DP\) 值爲權值 \(>W\) 的機率,得轉移爲:get

\[\large f_x=\prod_{y\in son_x}1-f_y \]

這樣作須要對每一個 \(k\)\(DP\) 一遍,複雜度沒法接受。所以將 \(k\) 也加到狀態中,對全部 \(k\) 一塊兒 \(DP\),用線段樹合併實現總體 \(DP\) 便可。it

同時發如今枚舉 \(k\) 的過程當中,每一個點是否要修改的狀況只會改變一次,所以也能用動態 \(DP\) 來優化。class

暴力 \(DP\),有 \(70\) 分。di

#include<bits/stdc++.h>
#define maxn 400010
#define inf 1000000000
#define p 998244353
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
	x=0;char c=getchar();bool flag=false;
	while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	if(flag)x=-x;
}
int n,L,R,k,pw=1;
int dep[maxn],val[maxn],ans[maxn];
ll f[maxn];
struct edge
{
	int  to,nxt;
	edge(int a=0,int b=0)
	{
		to=a,nxt=b;
	}
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
	e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt;
}
void dfs_pre(int x,int fa)
{
	val[x]=(dep[x]=dep[fa]+1)&1?-inf:inf;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==fa) continue;
		dfs_pre(y,x),val[x]=dep[x]&1?max(val[x],val[y]):min(val[x],val[y]);
	}
	if(abs(val[x])==inf) val[x]=x,pw=pw*2%p;
}
void dp(int x,int fa,int d)
{
	if(x==val[x])
	{
		if(abs(x-val[1])<k&&(d&1?x<val[1]:x>val[1])) f[x]=(p+1)>>1;
		else f[x]=dep[x]&1?x<val[1]:x>val[1];
		return;
	}
	f[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==fa) continue;
		dp(y,x,d),f[x]=f[x]*(1-f[y]+p)%p;
	}
}
void dfs(int x,int fa)
{
	f[x]=x==val[x]?(p+1)>>1:1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==fa) continue;
		if(val[x]==val[y]) dfs(y,x),f[1]=f[1]*f[y]%p;
		else dp(y,x,dep[x]),f[x]=f[x]*(1-f[y]+p)%p;
	}
}
int main()
{
	read(n),read(L),read(R);
	for(int i=1;i<n;++i)
	{
		int x,y;
		read(x),read(y);
		add(x,y),add(y,x);
	}
	dfs_pre(1,0);
	for(int i=max(L-1,1);i<=R;++i)
	{
		k=i;
		if(i==n) ans[i]=(pw-1+p)%p;
		else dfs(1,0),ans[i]=(1-f[1]+p)*pw%p;
		if(i>=L) printf("%d ",(ans[i]-ans[i-1]+p)%p);
	}
	return 0;
}

線段樹合併,總體 \(DP\)ant

#include<bits/stdc++.h>
#define maxn 400010
#define maxm 12000010
#define inf 1000000000
#define p 998244353
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
	x=0;char c=getchar();bool flag=false;
	while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	if(flag)x=-x;
}
int n,L,R,k,pw=1,tot;
int dep[maxn],val[maxn],ans[maxn],rt[maxn],ls[maxm],rs[maxm],add[maxm],mul[maxm];
struct edge
{
	int  to,nxt;
	edge(int a=0,int b=0)
	{
		to=a,nxt=b;
	}
}e[maxn];
int head[maxn],edge_cnt;
void link(int from,int to)
{
	e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt;
}
void dfs_pre(int x,int fa)
{
	val[x]=(dep[x]=dep[fa]+1)&1?-inf:inf;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==fa) continue;
		dfs_pre(y,x),val[x]=dep[x]&1?max(val[x],val[y]):min(val[x],val[y]);
	}
	if(abs(val[x])==inf) val[x]=x,pw=pw*2%p;
}
int get()
{
	mul[++tot]=1;
	return tot;
}
void pushadd(int &x,int v)
{
	if(!x) x=get();
	add[x]=(add[x]+v)%p;
}
void pushmul(int &x,int v)
{
	if(!x) x=get();
	add[x]=(ll)add[x]*v%p,mul[x]=(ll)mul[x]*v%p;
}
void pushdown(int x)
{
	if(mul[x]!=1) pushmul(ls[x],mul[x]),pushmul(rs[x],mul[x]),mul[x]=1;
	if(add[x]) pushadd(ls[x],add[x]),pushadd(rs[x],add[x]),add[x]=0;
}
void modify(int L,int R,int l,int r,int v,int &cur)
{
	if(!cur) cur=get();
	if(L<=l&&R>=r)
	{
		pushadd(cur,v);
		return;
	}
	pushdown(cur);
	if(L<=mid) modify(L,R,l,mid,v,ls[cur]);
	if(R>mid) modify(L,R,mid+1,r,v,rs[cur]);
}
int merge(int x,int y)
{
	if(!x&&!y) return 0;
	if(!ls[y]&&!rs[y])
	{
		pushmul(x,add[y]);
		return x;
	}
	if(!ls[x]&&!rs[x])
	{
		pushmul(y,add[x]);
		return y;
	}
	pushdown(x),pushdown(y);
	ls[x]=merge(ls[x],ls[y]),rs[x]=merge(rs[x],rs[y]);
	return x;
}
void solve(int l,int r,int cur)
{
	if(!ls[cur]&&!rs[cur])
	{
		for(int i=l;i<=r;++i) ans[i]=(ll)(1-add[cur]+p)*pw%p;
		return;
	}
	pushdown(cur),solve(l,mid,ls[cur]),solve(mid+1,r,rs[cur]);
}
void dp(int x,int fa,int d)
{
	if(x==val[x])
	{
		if(d&1?x<val[1]:x>val[1])
			modify(1,abs(x-val[1]),1,n-1,dep[x]&1?x<val[1]:x>val[1],rt[x]),modify(abs(x-val[1])+1,n-1,1,n-1,(p+1)>>1,rt[x]);
		else pushadd(rt[x],dep[x]&1?x<val[1]:x>val[1]);
		return;
	}
	pushadd(rt[x],1);
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==fa) continue;
		dp(y,x,d),pushmul(rt[y],p-1),pushadd(rt[y],1),rt[x]=merge(rt[x],rt[y]);
	}
}
void dfs(int x,int fa)
{
	pushadd(rt[x],x==val[x]?(p+1)>>1:1);
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==fa) continue;
		if(val[x]==val[y]) dfs(y,x),rt[1]=merge(rt[1],rt[y]);
		else dp(y,x,dep[x]),pushmul(rt[y],p-1),pushadd(rt[y],1),rt[x]=merge(rt[x],rt[y]);
	}
}
int main()
{
	read(n),read(L),read(R);
	for(int i=1;i<n;++i)
	{
		int x,y;
		read(x),read(y);
		link(x,y),link(y,x);
	}
	dfs_pre(1,0),ans[n]=(pw-1+p)%p,dfs(1,0),solve(1,n-1,rt[1]);
	for(int i=L;i<=R;++i) printf("%d ",(ans[i]-ans[i-1]+p)%p);
	return 0;
}
相關文章
相關標籤/搜索