1 条题解

  • 0
    @ 2026-5-7 21:57:40

    吐槽:有些题解打着“感性理解”的幌子给出容易引发歧义的描述,甚至还出现了错误的结论和复杂度错误的代码,素质不高。

    可以看出答案仅仅和奶牛之间的相对位置相关。对于两头奶牛 (i,j)(i, j),他们可能可以通过一系列操作交换位置,这时他们的相对位置就无关紧要;也有可能他们一定不能交换位置,此时他们就是独立的。定义图 G={(x,y)x,y can be swapped}G = \{(x, y) | x, y \ \text{can be swapped}\},则 GG 由若干连通块组成,我们关心的就是每个连通块的大小。

    (注意,整篇题解中我们都不关心哪头奶牛在哪个位置。这是因为仅仅知道所有连通块的大小就可以计算出答案。)

    尝试思考 (x,y)(x, y) 有边的条件,即 x,yx, y 为什么能被交换。我们刚学编程时就知道交换的道理是这样的:tx,xy,ytt \larr x, x \larr y, y \larr t。相似的,我们尝试在图上找到合适的 tt 节点,使得 x,yx, y 可以使用 tt 作为中转。

    一个明确的事实是链上不存在合适的 tt。比如你不可能交换下图的两头奶牛。(我们用加粗节点表示这上面有奶牛)

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

    1231 - 2 - 3 为一条链,两边看作子树。我们发现第一张图的左右两边存在可以交换的节点,但是第二张图不存在。究其原因,是奶牛太多了,填满了左右两棵子树,使得我们找不到合适的 tt 节点了。

    于是我们声称:令一条极长链(除了端点外节点度数均是 22)左右两棵子树的大小分别为 A,BA, B,则当 k<(A1)+(B1)k < (A - 1) + (B - 1) 时,一定存在某一对 (x,y)(x, y),使得 xx 在左子树,yy 在右子树,且 (x,y)G(x, y) \in G,此时称这条链为 11 类链;当 k(A1)+(B1)k \geq (A - 1) + (B - 1) 时,必然不存在这样的 (x,y)(x, y),此时称这条链为 22 类链。

    注意这个加粗的“某一对”。这表示并不是所有节点都能任意交换的,如下:

    这张图中 1111 号节点的奶牛不可能能和 7766 的奶牛换位。

    (有题解在这里给出的错误结论是“左右子树的节点可以任意换位”,还说要感性认识这一点,严谨证明看不懂之类的话。他当然看不懂,因为严谨证明是不存在的)

    现在我们可以感受到,一条 11 类链只说明了“链的两个端点的奶牛处于同一个连通块中”,若要分析两棵子树内部奶牛能否交换,还得递归分析这个子树的结构;而一条 22 类链直接否定了链两端任何节点相互连通的可能性,干脆利落。

    这引导我们从 22 类链入手分析:是否可以对于某一个点和一个固定的 kk,先假设这个点所在连通块大小就是 kk,然后再减去与这个点所在连通块相连接的 22 类链造成的一定不能在连通块中的节点呢?答案是肯定的。

    这是因为对于任意一种初始的奶牛状态和一条两边子树大小为 A,BA, B22 类链,我们一定能准确地指出,有 k(A1)k - (A - 1) 头奶牛必定不在左端点所在的连通块,且有 k(B1)k - (B - 1) 头奶牛必定不在右端点所在的连通块。由于 22 类链 k(A1)+(B1)k \geq (A - 1) + (B - 1) 的特性,你会发现这 k(A/B1)k - (A/B - 1) 头奶牛的编号是固定的,这就保证了不重不漏。

    考虑到一条链一条链的加比较麻烦,我们倒过来一条链一条链地删,这可以用并查集维护。总复杂度为 O(nlogn)O(n \log n),优化后可以做到 O(nα(n))O(n \alpha(n))

    /* 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
    上传者