[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;
}
posted @ 2023-05-18 15:56  DCH233  阅读(13)  评论(0编辑  收藏  举报