[ARC121F] Logical Operations on Tree
[ARC121F] Logical Operations on Tree
不妨先考虑怎样的方案是合法的。
我们有 and 和 or 运算的优良性质,考虑只操作叶子,如果出现叶子是 and 1 或者 or 0 很明显可以先删去。
然后策略就很明显了,可以贪心地先操作 and 0 操作,这样子最后才最有可能留下 1。
还注意到,如果过程中出现叶子 or 1,那么只需把剩下先全删掉在做 or 1 就合法了。
那么不难设计 dp 状态,设 \(f_{u,0/1/2}\) 表示子树内最终权值为 \(0/1\),和出现 or 1 的方案数。转移很平凡。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
namespace IO {
#define isdigit(x) (x >= '0' && x <= '9')
template<typename T>
inline void read(T &x) {
x = 0; char ch = getchar(); int f = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T, typename... Rest>
inline void read(T &x, Rest&... rest) {
read(x), read(rest...);
}
template<typename T>
inline void write(T x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
#undef isdigit
}
using namespace IO;
constexpr int N = 1e5 + 10;
int n;
vector<int> T[N];
constexpr int P = 998244353;
LL f[N][3];
void dfs(int u, int fa) {
f[u][0] = f[u][1] = 1;
for(int v : T[u]) {
if(v == fa) continue;
dfs(v, u);
LL x = f[u][0], y = f[u][1], z = f[u][2];
f[u][0] = (x * (2ll * f[v][0] + f[v][1]) % P + y * f[v][0]) % P;
f[u][1] = y * (f[v][0] + f[v][1]) % P;
f[u][2] = ((x + y) * (2ll * f[v][2] + f[v][1]) % P + 2ll * z * (f[v][0] + f[v][1] + f[v][2]) % P) % P;
}
}
int main() {
read(n);
for(int i = 1; i < n; ++i) {
int u, v;
read(u, v);
T[u].emplace_back(v), T[v].emplace_back(u);
}
dfs(1, 0);
printf("%d\n",(f[1][1] + f[1][2]) % P);
return 0;
}