题解:[ARC121F] Logical Operations on Tree
题意分析
显然树形 DP。
对于父节点 \(x\) 和子节点 \(v\) 之间的连边,考虑把 \(v\) 的信息合并到 \(x\) 上。
- 如果边是 \(\operatorname{or}0,\operatorname{and}1\),则没有影响,直接继承 \(f_{v,0}+f_{v,1}\) 种方案。
- 如果边是 \(\operatorname{and}0\),父节点一定会变成 \(0\),方案数 \(f_{v,0}\)。
- 如果是 \(\operatorname{or}1\),则可以发现此时全部合并到 \(v\) 上即为答案,一定有解。
第三种需要特殊判断,设 \(f_{x,0},f_{x,1}\) 分别表示子树内全部收缩到 \(x\),点权为 \(0,1\) 的方案数;\(f_{x,2}\) 表示子树内存在 \(\operatorname{or}1\) 的方案数。
转移不难把 \(v\) 合并到 \(x\) 上(记转移前 \(f\) 为 \(f'\)):
\[\begin{aligned}
f_{x,0}&\leftarrow f'_{x,0}(f_{v,0}+f_{v,1})+(f'_{x,0}+f'_{x,1})f_{v,0}\\
f_{x,1}&\leftarrow f'_{x,1}(f_{v,0}+f_{v,1})\\
f_{x,2}&\leftarrow (f'_{x,0}+f'_{x,1})f_{v,1}+2(f'_{x,0}+f'_{x,1})f_{v,2}+2f'_{x,2}(f_{v,0}+f_{v,1}+f_{v,2})
\end{aligned}
\]
带一个系数 \(2\) 是因为已经有解,\(\operatorname{and},\operatorname{or}\) 随便。
初始状态即 \(f_{x,0}=f_{x,1}=1,f_{x,2}=0\)。
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
#define int long long
constexpr const int N=1e5,P=998244353;
int n,f[N+1][3];
vector<int>g[N+1];
void dfs(int x,int fx){
f[x][0]=f[x][1]=1;
f[x][2]=0;
for(int v:g[x]){
if(v==fx){
continue;
}
dfs(v,x);
int f0[3]={f[x][0],f[x][1],f[x][2]};
f[x][0]=(f0[0]*(2ll*f[v][0]%P+f[v][1])%P+1ll*f0[1]*f[v][0]%P)%P;
f[x][1]=1ll*f0[1]*(f[v][0]+f[v][1])%P;
f[x][2]=((f0[0]+f0[1])%P*(2ll*f[v][2]+f[v][1])%P+2ll*f0[2]*(f[v][0]+f[v][1]+f[v][2])%P)%P;
}
}
main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,0);
cout<<(f[1][1]+f[1][2])%P<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号