CF917D Stranger Trees

复盘 \(\color{black}{\text{n}}\color{red}{\text{ealchen}}\) 神仙讲的数数题。

Warning:如果你做过「WC2019」数树 的话你可以尝试先不看题解,直接去想 \(O(n ^ 2)\) 的做法。

这题还算是弱化版的呢(

UPDATE:“不少于”仅是便于理解,更严谨的说法就是“钦定”,因为二项式反演内存在系数。其他比如子集反演就是真正意义上的“不少于”。

(感谢 \(\color{black}{\text{C}}\color{red}{\text{onstant}}\) 的纠正)

Description

给定一棵有 \(n\) 个点的树,对于所有 \(k \in [0, n - 1]\) ,求有 \(k\) 条边和模板树重合的树有多少个。

答案对 \(10 ^ 9 + 7\) 取模。

原题:\(n \leq 100\)

加强版:\(n\leq 5000\)

Analysis

我是不会告诉你我连 \(O(n ^ 4)\) 都不会做的(

大概思路就是利用 矩阵树 ,因为考虑到一般的 矩阵树 处理不了恰好 \(k\) 条边的类型。

所以我们可以利用类似 子集卷积 的思维,我们单独令树边的权值带上未知数 \(x\)

那么最终只需要在 矩阵树 上找 \(x ^ k\) 的地方就行了。

(大概是 多项式插值+矩阵树 之类的牛逼算法,我显然不会)

显然还不够精髓,看起来有点科技暴力(

那么假如我们已经钦定好 \(k\) 条边重合,根据树的性质,就还会剩下 \(n - k - 2\) 个联通块。

一个常用 trick:对于这样的森林,假定它们的大小是 \(siz_i\) ,它存在的树的方案数:

\[n ^ {n - k - 2} \prod_{i = 1} ^ k siz_i \]

(证明是用 矩阵树 或者 \(\text{Prufer}\) 序列,不会但是挺好记的)

那么其实对于有 \(k\) 条边重合的情况,需要知道的就是所有可能的 \(siz\)

复杂度直接上天。

考虑 DP。

Solution

(已经会和「WC2019」数树 sub2 大量相同了)

我们发现 DP 是能结合大量状态,但是却难以记录恰好 \(k\) 条边的限制。

有个比较自然的想法,恰好 \(k\) 条边不太行,但是改成钦定 \(k\) 条边呢?

这很二项式反演,令“恰好”是 \(f_k\) ,“钦定”是 \(g_k\)

\[g_k = \sum_{i = k} ^ n \binom{i}{k} f_i \]

\[f_k = \sum_{i = k} ^ n (-1) ^ {i - k} \binom{i}{k} g_i \]

于是我们设 \(dp_{u, i, k}\) 表示在 \(u\) 为根的子树中, \(u\) 所在联通块大小为 \(i\) ,且钦定了 \(k\) 条边重合的答案。(因为其他联通块还有重合所以即为钦定 \(k\) 条边

那么对于树上的每一条边 \((u, v)\) ,我们考虑选或者不选:

  1. 选:\(dp_{u, i + j, k + l + 1} = dp_{u, i, k} \cdot dp_{v, j, l}\)

  2. 不选:\(dp_{u, i, k + l} = dp_{u, i, k} \cdot dp_{v, j, l} \cdot l\)

因为枚举 \(u\)\(v\) 的子树最终只是 \(O(n ^ 2)\) 的。

然后总共是 \(O(n ^ 4)\) 的。


然后一通观察发现转移性是比较单一,多项式插值可以减掉一个 \(n\) ,但是我们并不满足。


考虑 trick 中 \(\prod_{i} a_i\) 组合意义,大概就是每个联通块选择一个“特殊点”的总方案数。

那其实对于当前联通快,我们并不关心其他地方究竟选了那些“特殊点”,只关心目前的联通块选了“特殊点”没有。

所以可以把联通快那一维替换成 \(0/1\)

注意:

  1. 这样的话假如边 \((u, v)\) 要合并,两个联通块不能同时选过“特殊点”的。(可以理解为“加量”)

  2. 假如不合并,必须要求 \(v\) 所在的联通快已经选过“特殊点”了(可以理解为“结算”)

直接 \(O(4)\) 转移,总时间就变到 \(O(n ^ 2)\) 的了。

Code

Code

/*

*/
#include 
using namespace std;

#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define Check(a) freopen(a".in", "r", stdin), freopen(a".ans", "w", stdout);

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair pii;
#define fi first
#define se second
#define mp std::make_pair

const int mod = 1e9 + 7;
template 
inline int M(A x) {return x;}
template 
inline int M(A x, B ... args) {return 1ll * x * M(args...) % mod;}

const int N = 5e3 + 10;

int n, fst[N], tot;
struct edge {int nxt, to;} e[N << 1];

inline void add(int u, int v) {
	e[++tot] = (edge) {fst[u], v}; fst[u] = tot;
	e[++tot] = (edge) {fst[v], u}; fst[v] = tot;
}

int f[N][N][2], g[N][N], si[N];

inline void dfs(int u, int fa) {
	f[u][0][0] = f[u][0][1] = si[u] = 1;

	for (int i = fst[u], v; i; i = e[i].nxt) {
		v = e[i].to;
		if (v == fa) continue;
		dfs(v, u);

		for (int j = 0; j <= si[u]; ++j) {
			for (int k = 0; k <= n - si[u] && k <= si[v]; ++k) {
				for (int x = 0; x < 2; ++x) {
					for (int y = 0; x + y < 2; ++y) {
						g[j + k + 1][x | y] += M(f[u][j][x], f[v][k][y]);
						(g[j + k + 1][x | y] >= mod) && (g[j + k + 1][x | y] -= mod);
					}
					g[j + k][x] += M(f[u][j][x], f[v][k][1]);
					(g[j + k][x] >= mod) && (g[j + k][x] -= mod);
				}
			}
		}
		
		si[u] += si[v];
		for (int j = 0; j <= si[u]; ++j) {
			for (int k = 0; k < 2; ++k) {
				f[u][j][k] = g[j][k]; g[j][k] = 0;
			}
		}
	}
}

int ret[N], res = 1, C[N][N];

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	std::cin >> n;
	for (int i = 1, u, v; i < n; ++i) {
		std::cin >> u >> v; add(u, v);
	}
	
	dfs(1, 0); ret[n - 1] = 1;
	for (int i = n - 2; ~i; --i) {
		ret[i] = M(res, f[1][i][1]); res = M(res, n);
	}

	for (int i = 0; i < n; ++i) {
		int ans = 0;

		for (int j = i; j <= n; ++j) {
			if (!i) C[j][i] = 1;
			else {
				C[j][i] = C[j - 1][i - 1] + C[j - 1][i];
				(C[j][i] >= mod) && (C[j][i] -= mod);
			}
			
			res = M(C[j][i], ret[j]);

			if ((j - i) & 1) ans += mod - res; else ans += res;
			(ans >= mod) && (ans -= mod);
		}

		std::cout << ans << " ";
	}

	return 0;
}


posted @ 2022-07-11 10:03  Illusory_dimes  阅读(27)  评论(0编辑  收藏  举报