题解:P6277 [USACO20OPEN] Circus P
发现交换操作有可传递性:如果
我们发现如果当前如果有
所以我们固定这
假如
我们设这些团大小为
考虑怎样的情况下两头牛是可以交换的:
首先,我们可以把牛从位置
然后在不影响其它牛的情况下交换两头牛。
最后,按照相反的顺序移动回位置
此时,两头牛就是可交换的,连一条边。
考虑怎样在不影响其它牛的情况下交换两头牛:
发现当在一条没有牛的链且出现一个分支时,两头牛可以交换:
如上,位置
总结可以交换的规律:
- 没有牛的链;
- 有一个分支(有一个位置度数大于二)。
我们将其拓展一下:
在一个中间都是二度点,两端都是非二度点的链上,设两端不包括链的树的大小分别为
如上,
我们发现,当
原理:将
总结可以交换的规律:
发现,若
也就是说,我们链的两端可以互相连接形成很多个连通块。
我们可以把每个满足交换条件,也就是
因为当
回到刚刚那个问题,怎么计算能到达这些点的牛的个数?
正難则反,我们可以计算出无法到达这个连通块的牛的个数,再用牛的总数
如果
那么我们能够进入到这个联通块的最多只有
那么总共的可到达的节点数量为:
发现
设连通块大小为
我们维护一下连通块的
因为每一次连通块个数等于不合法链的数量加上一,而不合法链只能存活链长的时间,所以均摊之后是 set 维护。所以时间复杂度为
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100010
#define P 1000000007
ll n;
ll deg[N];
ll head[N], nxt[2 * N], to[2 * N], cnt;
ll fa[N], out[N], siz[N];
ll ans[N], fac[N], ifac[N];
set<ll> s;
struct node {
ll a, b, c;
node(ll a = 0, ll b = 0, ll c = 0):a(a), b(b), c(c) {}
} edge[N];
ll tot;
void addEdge(ll u, ll v) {
cnt ++;
to[cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
}
ll find(ll x) {
if(fa[x] == x) {
return x;
}
return fa[x] = find(fa[x]);
}
void merge(ll a, ll b, ll c) {
a = find(a), b = find(b);
fa[a] = b;
siz[b] += siz[a] + c - 2;
out[b] += out[a] - 2;
s.erase(a);
}
void dfs(ll u, ll fa, ll rt, ll c) {
if(deg[u] != 2) {
if(rt) {
edge[++ tot] = node(u, rt, c);
}
c = 1;
rt = u;
}
for(ll i = head[u]; i; i = nxt[i]) {
ll v = to[i];
if(v == fa) continue;
dfs(v, u, rt, c + 1);
}
}
ll qpow(ll x, ll y) {
if(y == 0) return 1;
if(y % 2 == 1) return x * qpow(x, y - 1) % P;
ll tmp = qpow(x, y / 2);
return tmp * tmp % P;
}
int main() {
scanf("%lld", &n);
for(ll k = 1; k < n; k ++) {
ll u, v;
scanf("%lld %lld", &u, &v);
addEdge(u, v);
addEdge(v, u);
deg[u] ++;
deg[v] ++;
}
for(ll i = 1; i <= n; i ++) {
if(deg[i] != 2) {
fa[i] = i;
siz[i] = 1;
out[i] = deg[i];
s.insert(i);
}
}
dfs(*s.begin(), 0, 0, 0);
sort(edge + 1, edge + 1 + tot, [&](const auto &x, const auto &y) {
if(x.c == y.c) {
if(x.a == y.a) return x.b < y.b;
else return x.a < y.a;
}
return x.c < y.c;
});
fac[0] = 1;
for(ll i = 1; i <= n; i ++) {
fac[i] = fac[i - 1] * i % P;
}
ifac[n] = qpow(fac[n], P - 2);
for(ll i = n; i >= 1; i --) {
ifac[i - 1] = ifac[i] * i % P;
}
ans[n] = fac[n];
ll pos = 1;
for(ll k = n - 1; k >= 1; k --) {
while(pos <= tot && k < n - edge[pos].c) {
merge(edge[pos].a, edge[pos].b, edge[pos].c);
pos ++;
}
ans[k] = fac[k];
for(ll i : s) {
(ans[k] *= ifac[(out[i] - 1) * (n - k - 1) + siz[i] - 1]) %= P;
}
}
for(ll i = 1; i <= n; i ++) {
printf("%lld\n", ans[i]);
}
}