1 条题解
-
0
吐槽:有些题解打着“感性理解”的幌子给出容易引发歧义的描述,甚至还出现了错误的结论和复杂度错误的代码,素质不高。
可以看出答案仅仅和奶牛之间的相对位置相关。对于两头奶牛 ,他们可能可以通过一系列操作交换位置,这时他们的相对位置就无关紧要;也有可能他们一定不能交换位置,此时他们就是独立的。定义图 ,则 由若干连通块组成,我们关心的就是每个连通块的大小。
(注意,整篇题解中我们都不关心哪头奶牛在哪个位置。这是因为仅仅知道所有连通块的大小就可以计算出答案。)
尝试思考 有边的条件,即 为什么能被交换。我们刚学编程时就知道交换的道理是这样的:。相似的,我们尝试在图上找到合适的 节点,使得 可以使用 作为中转。
一个明确的事实是链上不存在合适的 。比如你不可能交换下图的两头奶牛。(我们用加粗节点表示这上面有奶牛)

当存在度数 的节点时,才存在交换的可能。比如下面这两张图:


为一条链,两边看作子树。我们发现第一张图的左右两边存在可以交换的节点,但是第二张图不存在。究其原因,是奶牛太多了,填满了左右两棵子树,使得我们找不到合适的 节点了。
于是我们声称:令一条极长链(除了端点外节点度数均是 )左右两棵子树的大小分别为 ,则当 时,一定存在某一对 ,使得 在左子树, 在右子树,且 ,此时称这条链为 类链;当 时,必然不存在这样的 ,此时称这条链为 类链。
注意这个加粗的“某一对”。这表示并不是所有节点都能任意交换的,如下:

这张图中 号节点的奶牛不可能能和 或 的奶牛换位。
(有题解在这里给出的错误结论是“左右子树的节点可以任意换位”,还说要感性认识这一点,严谨证明看不懂之类的话。他当然看不懂,因为严谨证明是不存在的)
现在我们可以感受到,一条 类链只说明了“链的两个端点的奶牛处于同一个连通块中”,若要分析两棵子树内部奶牛能否交换,还得递归分析这个子树的结构;而一条 类链直接否定了链两端任何节点相互连通的可能性,干脆利落。
这引导我们从 类链入手分析:是否可以对于某一个点和一个固定的 ,先假设这个点所在连通块大小就是 ,然后再减去与这个点所在连通块相连接的 类链造成的一定不能在连通块中的节点呢?答案是肯定的。
这是因为对于任意一种初始的奶牛状态和一条两边子树大小为 的 类链,我们一定能准确地指出,有 头奶牛必定不在左端点所在的连通块,且有 头奶牛必定不在右端点所在的连通块。由于 类链 的特性,你会发现这 头奶牛的编号是固定的,这就保证了不重不漏。
考虑到一条链一条链的加比较麻烦,我们倒过来一条链一条链地删,这可以用并查集维护。总复杂度为 ,优化后可以做到 。
/* K_crane x N_Cat */ #include <bits/stdc++.h> #define lowbit(x) ((x) & (-(x))) using namespace std; const int N = 100010, mod = 1e9 + 7; typedef pair < int, int > PAIR; long long fac[N], inv[N]; inline long long qpow(long long a, long long b) { long long res = 1; while(b) { if(b & 1) res = res * a % mod; b >>= 1, a = a * a % mod; } return res; } inline long long C(int n, int m) {return fac[n] * inv[m] % mod * inv[n - m] % mod;} inline void init_math() { fac[0] = inv[0] = 1; for(int i = 1; i <= 100000; ++i) fac[i] = fac[i - 1] * i % mod; inv[100000] = qpow(fac[100000], mod - 2); for(int i = 99999; i >= 1; --i) inv[i] = inv[i + 1] * (i + 1) % mod; } int T = 1, n, deg[N], fa[N][22], siz[N], dep[N]; vector < int > tr[N]; long long res[N]; int seq[N * 2][22], sn, lft[N]; inline int LCA(int x, int y) { if(lft[x] > lft[y]) swap(x, y); int k = __lg(lft[y] - lft[x] + 1); if(dep[seq[lft[x]][k]] <= dep[seq[lft[y] - (1 << k) + 1][k]]) return seq[lft[x]][k]; else return seq[lft[y] - (1 << k) + 1][k]; } inline int get_dist(int x, int y) {return dep[x] + dep[y] - 2 * dep[LCA(x, y)];} inline void init_LCA() { for(int i = 1; (1 << i) <= sn; ++i) { for(int j = 1; j + (1 << i) - 1 <= sn; ++j) { if(dep[seq[j][i - 1]] <= dep[seq[j + (1 << (i - 1))][i - 1]]) seq[j][i] = seq[j][i - 1]; else seq[j][i] = seq[j + (1 << (i - 1))][i - 1]; } } for(int i = sn; i >= 1; --i) lft[seq[i][0]] = i; } inline int jump(int x, int dist) { while(dist) x = fa[x][__lg(lowbit(dist))], dist -= lowbit(dist); return x; } inline PAIR get(int x, int y) { if(LCA(x, y) == x) return {n - siz[jump(y, dep[y] - dep[x] - 1)], siz[y]}; else if(LCA(x, y) == y) return {siz[x], n - siz[jump(x, dep[x] - dep[y] - 1)]}; else return {siz[x], siz[y]}; } struct Path { int x, y; inline int dist() {return get_dist(x, y);} } path[N]; int pn; inline bool cmp(Path u, Path v) {return u.dist() < v.dist();} int tag[N]; struct DSU { int prt[N], siz[N]; long long w[N], num[N]; inline void init(int n) {for(int i = 1; i <= n; ++i) prt[i] = i, siz[i] = 1;} inline int find(int x) {return ((prt[x] == x) ? x : (prt[x] = find(prt[x])));} inline void merge(int x, int y) { x = find(x), y = find(y); if(x != y) { if(siz[x] > siz[y]) swap(x, y); prt[x] = y, siz[y] += siz[x], w[y] += w[x], num[y] += num[x]; w[x] = num[x] = 0; } } }; DSU dsu; inline long long calc(int k, int id) { if(tag[id] == k) return 1; tag[id] = k; return inv[k - (k * dsu.num[id] - dsu.w[id])]; } inline void init(int pos, int prt) { fa[pos][0] = prt, siz[pos] = 1; for(int i = 1; ; ++i) { if(fa[fa[pos][i - 1]][i - 1]) fa[pos][i] = fa[fa[pos][i - 1]][i - 1]; else break; } seq[++sn][0] = pos, dep[pos] = dep[prt] + 1; for(int to : tr[pos]) { if(to == prt) continue; init(to, pos), siz[pos] += siz[to]; seq[++sn][0] = pos; } } inline void search(int pos, int prt, int st) { if(deg[pos] != 2) { if(st < pos) path[++pn] = {st, pos}; return ; } for(int to : tr[pos]) { if(to == prt) continue; search(to, pos, st); } } inline void sol() { cin >> n; for(int i = 1, x, y; i < n; ++i) { cin >> x >> y; ++deg[x], ++deg[y]; tr[x].push_back(y); tr[y].push_back(x); } init(1, 0); init_LCA(); dsu.init(n); for(int i = 1; i <= n; ++i) if(deg[i] != 2) for(int j : tr[i]) search(j, i, i); sort(path + 1, path + pn + 1, cmp); for(int i = 1; i <= pn; ++i) { PAIR cur = get(path[i].x, path[i].y); dsu.w[dsu.find(path[i].x)] += (cur.first - 1); dsu.w[dsu.find(path[i].y)] += (cur.second - 1); ++dsu.num[dsu.find(path[i].x)]; ++dsu.num[dsu.find(path[i].y)]; } res[n] = fac[n], res[n - 1] = fac[n - 1]; for(int k = n - 2, pt = 1; k >= 1; --k) { res[k] = fac[k]; if(pt == pn + 1) res[k] = 1; for(int i = pt; i <= pn; ++i) { res[k] = res[k] * calc(k, dsu.find(path[i].x)) % mod; res[k] = res[k] * calc(k, dsu.find(path[i].y)) % mod; } while(pt <= pn && path[pt].dist() + 1 == n - k) { PAIR cur = get(path[pt].x, path[pt].y); dsu.w[dsu.find(path[pt].x)] -= (cur.first - 1); dsu.w[dsu.find(path[pt].y)] -= (cur.second - 1); --dsu.num[dsu.find(path[pt].x)]; --dsu.num[dsu.find(path[pt].y)]; dsu.merge(path[pt].x, path[pt].y); ++pt; } } for(int i = 1; i <= n; ++i) cout << res[i] << '\n'; } int main() { // freopen("text.in", "r", stdin); // freopen("prog.out", "w", stdout); ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); init_math(); while(T--) sol(); return 0; } /* */
- 1
信息
- ID
- 6869
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 21
- 已通过
- 6
- 上传者