CF1554E You
https://www.luogu.com.cn/problem/CF1554E
VP的时候不会做
丢脸……
确实还是有一点难度的
题解
考虑问题可以转换为给每条边定向,然后
a
[
u
]
a[u]
a[u]表示的是
u
u
u的入度
可以完美的和原问题构成双射,总的方案数就是
2
n
−
1
2^{n-1}
2n−1
然后发现入度总和是
n
−
1
n-1
n−1,所以只有
k
∣
(
n
−
1
)
k|(n-1)
k∣(n−1)的时候才有值
先考虑
K
>
1
K>1
K>1
发现值最大为1,因为叶子节点只能指向父亲,然后每个点的入度和必须为K的倍数,那么每条边的方向就定下来了
然后对于
K
=
1
K=1
K=1的用总方案数减去即可
code:
#include<bits/stdc++.h>
#define N 200050
#define ll long long
#define mod 998244353
using namespace std;
struct edge {
int v, nxt;
} e[N << 1];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v) {
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid ++;
}
int K, n, a[N], t, ans[N];
void dfs(int u, int fa) {
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
dfs(v, u);
}
if(a[u] % K == 0) a[fa] ++;
else a[u] ++;
}
int qpow(ll x, int y) {
ll ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
int gcd(int x, int y) {
return y? gcd(y, x % y) : x;
}
int check(int k) {
K = k;
for(int i = 0; i <= n; i ++) a[i] = 0;
dfs(1, 0); int d = 0;
for(int i = 1; i <= n; i ++) d = gcd(d, a[i]);
// for(int i = 1; i <= n; i ++) printf("%d ", a[i]); printf(" **%d %d\n", k, d);
return d == k;
}
int main() {
scanf("%d", &t);
while(t --) {
init();
scanf("%d", &n);
for(int i = 1; i <= n; i ++) ans[i] = 0;
for(int i = 1; i < n; i ++) {
int u, v;
scanf("%d%d", &u, &v);
insert(u, v), insert(v, u);
}
for(int k = 1; k * k < n; k ++) if((n - 1) % k == 0){
if(k > 1) ans[k] = check(k); ans[(n - 1) / k] = check((n - 1) / k);
}
ans[1] = qpow(2, n - 1);
for(int i = 2; i < n; i ++) ans[1] = (ans[1] - ans[i] + mod) % mod;
for(int i = 1; i <= n; i ++) printf("%d ", ans[i]); printf("\n");
}
return 0;
}