「解题报告」P6277 [USACO20OPEN] Circus P
首先容易发现每种等价类的大小一定是相等的,那么最后答案就一定是
我们考虑两头牛之间能不能交换,发现这种关系是具有传递性的,那么交换的关系图一定会形成若干个团,一个团之间是可以任意交换的。那么最后答案就是
我们考虑什么时候能交换,容易想到一种简单的交换就是在一个度数为三的点上去换。这是一个最基本的交换,只要在任意一个度数大于等于
而整棵树由若干关键点划分成了若干条链,我们考虑一条由两个关键点连起来的一条链上能否进行交换。我们设两端两个关键点的子树大小分别为
首先考虑到我们的问题是能够使得两头牛交换且不改变其它牛的位置,但是不改变其它牛的位置这个限制实际上是没有意义的,因为如果某一刻能够使得两头牛交换位置,那么我们再按照一模一样的移动倒着就能把其它所有牛全部还原回去,所以我们实际上只用考虑某一刻能否将考虑的两头牛交换即可。
那么我们考虑链上两头相邻的牛能否交换。直观的想法肯定是让这两个点左边的牛全部往左边的子树里填,右边的牛全部往右边的子树里填,这样这两头牛可以去任意一个关键点的地方去调换位置。而子树内可以包含的牛是有限的,注意到如果左右两棵子树全都填满了,那么此时这两头牛就不能交换了,这时一定有
如果
总结一下,对于一条链,如果
这时候问题就清晰了许多了。我们称满足
首先链合法的条件可以改写成
团的大小并不好计算,但是我们是容易计算团外面的点数的,于是简单容斥一步就可以了。对于团外面的点数,我们考虑连通块往外连的每一条不合法链,设连通块外的子树为 set 维护所有的连通块,每次遍历所有连通块计算答案即可。时间复杂度
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005, P = 1000000007;
int n;
vector<int> e[MAXN];
set<int> s;
int fa[MAXN], out[MAXN], siz[MAXN];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int u, int v, int w) {
u = find(u), v = find(v);
fa[u] = v, siz[v] += siz[u] + w - 2, out[v] += out[u] - 2;
s.erase(u);
}
vector<tuple<int, int, int>> ed;
void dfs(int u, int pre, int rt, int dep) {
if (e[u].size() != 2) {
if (rt) ed.push_back({ dep, rt, u });
rt = u, dep = 1;
}
for (int v : e[u]) if (v != pre) dfs(v, u, rt, dep + 1);
}
int qpow(int a, int b) {
int ans = 1;
while (b) {
if (b & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P;
b >>= 1;
}
return ans;
}
int fac[MAXN], inv[MAXN];
int ans[MAXN];
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
for (int i = 1; i <= n; i++) if (e[i].size() != 2) s.insert(i), fa[i] = i, out[i] = e[i].size(), siz[i] = 1;
dfs(*s.begin(), 0, 0, 1);
fac[0] = 1;
for (int i = 1; i <= n; i++) fac[i] = 1ll * i * fac[i - 1] % P;
inv[n] = qpow(fac[n], P - 2);
for (int i = n; i >= 1; i--) inv[i - 1] = 1ll * inv[i] * i % P;
ans[n] = fac[n];
sort(ed.begin(), ed.end());
auto it = ed.begin();
for (int k = n - 1; k >= 1; k--) {
while (it != ed.end() && k < n - get<0>(*it)) merge(get<1>(*it), get<2>(*it), get<0>(*it)), it++;
ans[k] = fac[k];
for (int i : s) ans[k] = 1ll * ans[k] * inv[(n - 1 - k) * (out[i] - 1) + siz[i] - 1] % P;
}
for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);
return 0;
}