ARC121F Logical Operations on Tree
ARC121F Logical Operations on Tree
下文叶子定义为树中最外层的点。
大大的性质:考虑当一个叶子为 时,那么我们只要最后操作这个叶子,无论如何最后都可以得到 。
再思考合法树中叶子的其他情况:
- 当叶子为 时,它头上的树合法。
- 当叶子为 时,它头上的树能够得到 ,即合法。
- 当叶子为 时,它操作之前之后的树合法。
综上,只有 比较特殊,存在即合法,其他情况都有“原树合法”的大前提。
注意到,出现 这种叶子和头上树合法两种限制满足任意一种即合法。
故考虑容斥,统计没有出现 这种叶子的树的数量以及这些树中是合法树的数,最后用总方案 没有出现 这种叶子的树的数量 没有出现 这种叶子的树且合法的树的数量得到答案,即为总方案 没有出现 这种叶子且不合法的情况。
记 表示 的子树中没有出现 这种叶子的树的数量, 表示 的子树中没有出现 这种叶子的树且合法的树的数量。
初始化
对于 ,有转移
乘 是因为边 有 、 两种可能,减去 是为了去除子树合法且 为 的情况。
对于 ,有转移
注意到这里要求必为合法且无 ,所以儿子的每种取值都有唯一对应,如果是 ,那么边必须为 ,如果子树是 ,那么边必须为 。
最后答案就是
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
#define ha putchar(' ')
#define he putchar('\n')
inline int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x * f;
}
inline void write(int x)
{
if(x < 0)
{
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + 48);
}
const int _ = 1e5 + 10, mod = 998244353;
int n, f[_], g[_];
vector<int> d[_];
int qpow(int y)
{
int res = 1, x = 2;
while(y)
{
if(y & 1) res = res * x % mod;
x = x * x % mod, y >>= 1;
}
return res;
}
void dfs(int u, int fa)
{
f[u] = 2, g[u] = 1;
for(int v : d[u]) if(v != fa)
{
dfs(v, u);
f[u] = f[u] * ((f[v] << 1) % mod + mod - g[v]) % mod;
g[u] = g[u] * f[v] % mod;
}
}
signed main()
{
n = read();
for(int i = 1, u, v; i < n; ++i)
{
u = read(), v = read();
d[u].push_back(v), d[v].push_back(u);
}
dfs(1, 0);
write((qpow((n << 1) - 1) + mod - f[1] + g[1]) % mod);
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18121979