问题可以转化成给每个边定向, 变成了 点的入度。
因为给每个点定向必然有个点的入度为 ,从这个点开始删即为一种合法删点方案;对于每个删点方案,每删一个点就把相邻的边都定向到自己,这样本质不同的删点方案一定当且仅当存在一个点的入度不同。这样构造了一个双射,完成了问题的转化。
所以答案的总和为 。
入度的总数为 ,所以一定只有 的因数答案才不为 。
考虑如果 ,怎样判断答案是不是 ?叶子是可以直接确定边要往上的,因为不能出现入度为 ,这样可以把叶子删去,删去叶子之后的叶子,挂着它的边也能定向,然后这个叶子也可以删去...这样不断做拓扑排序,即可以构造出一个方案,看这个方案满不满足条件即可。
注意到这个方案数唯一的,所以 时直接拓扑排序来判断即可。
直接这么做时间复杂度是 ,有一个显著的优化是如果只判素因子就行,再把判素因子得到的 的 ans 加上 。
的答案的话,用答案的总数 减去其它所有的答案就行。复杂度是 , 是 的素因子个数。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?