CF917D Stranger Trees
复盘
Warning:如果你做过「WC2019」数树 的话你可以尝试先不看题解,直接去想
这题还算是弱化版的呢(
UPDATE:“不少于”仅是便于理解,更严谨的说法就是“钦定”,因为二项式反演内存在系数。其他比如子集反演就是真正意义上的“不少于”。
(感谢
Description#
给定一棵有
答案对
原题:
加强版:
Analysis#
我是不会告诉你我连
大概思路就是利用 矩阵树 ,因为考虑到一般的 矩阵树 处理不了恰好
所以我们可以利用类似 子集卷积 的思维,我们单独令树边的权值带上未知数
那么最终只需要在 矩阵树 上找
(大概是 多项式插值+矩阵树 之类的牛逼算法,我显然不会)
显然还不够精髓,看起来有点科技暴力(
那么假如我们已经钦定好
一个常用 trick:对于这样的森林,假定它们的大小是
(证明是用 矩阵树 或者
那么其实对于有
复杂度直接上天。
考虑 DP。
Solution#
(已经会和「WC2019」数树 sub2 大量相同了)
我们发现 DP 是能结合大量状态,但是却难以记录恰好
有个比较自然的想法,恰好
这很二项式反演,令“恰好”是
于是我们设
那么对于树上的每一条边
-
选:
-
不选:
因为枚举
然后总共是
然后一通观察发现转移性是比较单一,多项式插值可以减掉一个
考虑 trick 中
那其实对于当前联通快,我们并不关心其他地方究竟选了那些“特殊点”,只关心目前的联通块选了“特殊点”没有。
所以可以把联通快那一维替换成
注意:
-
这样的话假如边
要合并,两个联通块不能同时选过“特殊点”的。(可以理解为“加量”) -
假如不合并,必须要求
所在的联通快已经选过“特殊点”了(可以理解为“结算”)
直接
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?