ARC121F - Logical Operations on Tree

ARC121F - Logical Operations on Tree

题目大意

给定一棵树,现在对于每一个点加上权值\(a_i\in\{0,1\}\),每一条边加上操作\(opt_i\in\{\vee,\wedge\}\)

每次操作选择一条边收缩两边的点,权值为两者操作的结果

对于所有\(2^{2n-1}\)种方案,求出所有 存在一种收缩顺序使得最终剩余的点权值为1的 方案数


分析

一开始我以为先做\(\vee\),然后做\(\wedge\),然后发现\(1 - \vee - 0 - \wedge -0\)就卡掉了

仔细分析实际上是:

如果一个0旁边有一个\(\wedge\),那么这个0通过操作一定会把另一边的值赋为\(0\)

为了浪费尽量少的1,这样的操作应该先做


因此,贪心的策略是:

1.先一直收缩到所有\(\wedge\)旁边都是1

2.然后任意顺序做

所以实际上最优决策是先做\(\wedge\)再做\(\vee\)

那么一个方案合法的判定:

存在一个\(\wedge\)边连成的联通块,其中所有数都是1

const int N=1e5+10,P=998244353;

int n;
vector <int> G[N];
int dp[N][2];
void dfs(int u,int f){
	dp[u][0]=dp[u][1]=1;
	for(int v:G[u]) if(v!=f) {
		dfs(v,u);
		int t[2]={0,0};
		rep(i,0,1) rep(j,0,1) t[i&j]=(t[i&j]+1ll*dp[u][i]*dp[v][j])%P;
		rep(i,0,1) t[i]=(t[i]+1ll*dp[u][i]*dp[v][0])%P;
		dp[u][0]=t[0],dp[u][1]=t[1];
	}
}

int main(){
	n=rd();
	rep(i,2,n) {
		int u=rd(),v=rd();
		G[u].pb(v),G[v].pb(u);
	}
	dfs(1,0);
	int ans=1;
	rep(i,1,2*n-1) ans=ans*2%P;
	printf("%d\n",(ans-dp[1][0]+P)%P);
}
posted @ 2021-05-30 20:32  chasedeath  阅读(270)  评论(0编辑  收藏  举报