洛谷P5281 [ZJOI2019] Minimax搜索

设修改前根节点的权值为 (W),不难发现,一定存在一条从根到叶子的链,链上的每个点权值都为 (W),那么只要这条链上任意一点权值改变,根节点权值最后一定会改变。

考虑一个叶子 (i),且 (w_i eq W),要想通过改变该点权值来让链上的点发生改变,该点必须满足以下一个情况:

(w_i<W),且该点祖先和链的第一个交点的深度为奇数,这时将其改为 (W+1)

(w_i > W),且该点祖先和链的第一个交点的深度为偶数,这时将其改为 (W-1)

直接处理代价为 (k) 的情况不好处理,考虑求出代价 (leqslant k) 的方案后差分。

将问题转化为概率来考虑,链上的点设 (f_x) 表示 (x) 子树内的叶子通过代价 (leqslant k) 的修改后 (x) 权值不变的概率,得最终方案数为总方案乘 (1-prod f_x)。考虑不在链上的点,若其深度为奇数,(DP) 值为权值 (<W) 的概率,若其深度为偶数,(DP) 值为权值 (>W) 的概率,得转移为:

[large f_x=prod_{yin son_x}1-f_y ]

这样做需要对每个 (k)(DP) 一遍,复杂度无法接受。因此将 (k) 也加到状态中,对所有 (k) 一起 (DP),用线段树合并实现整体 (DP) 即可。

同时发现在枚举 (k) 的过程中,每个点是否要修改的情况只会改变一次,因此也能用动态 (DP) 来优化。

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

#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)

#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;
}
原文地址:https://www.cnblogs.com/lhm-/p/14435988.html