「题解」Codeforces 1554E You
问题可以转化成给每个边定向,\(a_i\) 变成了 \(i\) 点的入度。
因为给每个点定向必然有个点的入度为 \(0\),从这个点开始删即为一种合法删点方案;对于每个删点方案,每删一个点就把相邻的边都定向到自己,这样本质不同的删点方案一定当且仅当存在一个点的入度不同。这样构造了一个双射,完成了问题的转化。
所以答案的总和为 \(2^{n-1}\)。
入度的总数为 \(n-1\),所以一定只有 \(n-1\) 的因数答案才不为 \(0\)。
考虑如果 \(k>1\),怎样判断答案是不是 \(0\)?叶子是可以直接确定边要往上的,因为不能出现入度为 \(1\),这样可以把叶子删去,删去叶子之后的叶子,挂着它的边也能定向,然后这个叶子也可以删去...这样不断做拓扑排序,即可以构造出一个方案,看这个方案满不满足条件即可。
注意到这个方案数唯一的,所以 \(k>1\) 时直接拓扑排序来判断即可。
直接这么做时间复杂度是 \(\mathcal{O}(n\sqrt n)\),有一个显著的优化是如果只判素因子就行,再把判素因子得到的 \(\gcd\) 的 ans 加上 \(1\)。
\(k=1\) 的答案的话,用答案的总数 \(2^{n-1}\) 减去其它所有的答案就行。复杂度是 \(\mathcal{O}(n \omega (n))\),\(\omega (n)\) 是 \(n\) 的素因子个数。
#include<iostream>
#include<cstdio>
#include<queue>
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T& read(T& r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
typedef long long ll;
const ll mod = 998244353;
ll qpow(ll x, ll y) {
ll sumq = 1;
while(y) {
if(y & 1) sumq = sumq * x % mod;
x = x * x % mod;
y >>= 1;
}return sumq;
}
#define int long long
const int N = 1000010;
int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
int n, head[N], ent, ans[N], a[N], in[N], in_[N], b[N];
bool vis[N];
struct Edge {
int to, next;
}e[N << 1];
inline void add(int x, int y) {
e[++ent].to = y; e[ent].next = head[x]; head[x] = ent;
}
std::queue<int>q;
int check(int x) {
for(int i = 1; i <= n; ++i) b[i] = a[i] = 0, in[i] = in_[i], vis[i] = 0;
for(int i = 1; i <= n; ++i)
if(in[i] == 1) {
q.push(i);
}
while(!q.empty()) {
int y = q.front();q.pop(); vis[y] = 1;
for(int i = head[y]; i;i = e[i].next) {
int v = e[i].to;
if(vis[v] == 1) continue ;
if(a[y] == 0) ++a[v], a[v] %= x, ++b[v];
else ++a[y], a[y] %= x, ++b[y];
--in[v];
if(in[v] == 1) {
q.push(v);
}
}
}
int g = b[1];
for(int i = 2; i <= n; ++i) g = gcd(g, b[i]);
return g;
}
void solve() {
read(n);
for(int i = 1; i <= n; ++i) ans[i] = in_[i] = 0, head[i] = 0;
ent = 0;
for(int i = 1, u, v; i < n; ++i) read(u), read(v), add(u, v), add(v, u), in_[u]++, in_[v]++;
int m = n-1;
for(int i = 1; i * i <= m; ++i)
if(m % i == 0) {
int t = check(i);
if(t % i == 0) ans[t] = 1;
while(i != 1 && m % i == 0) m /= i;
}
if(m > 1) {
int t = check(m);
if(t % m == 0) ans[t] = 1;
}
ans[1] = qpow(2, n-1);
for(int i = 2; i <= n; ++i) ans[1] -= ans[i];
for(int i = 1; i <= n; ++i) printf("%lld ", ans[i]);
puts("");
}
signed main() {
int T; read(T);
while(T--)
solve();
return 0;
}