ARC121F - Logical Operations on Tree
题目大意
给定一棵树,现在对于每一个点加上权值(a_iin{0,1}),每一条边加上操作(opt_iin{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
",(ans-dp[1][0]+P)%P);
}