【ARC101C】 Ribbons on Tree 题解 (容斥 + 树形 dp)
LG 传送门:AT4352 [ARC101C] Ribbons on Tree
先放兔队的题解。
然后解释一下细节:为什么可以每个连通块内随意连边?这就需要注意容斥的一大特点了——除了集合
《转移也是显然的》:(兔队题解中的
for(auto v : e[u]) if(v ^ fa){
tree_dp(v, u); rep(i, 1, sz[u] + sz[v]) t[i] = 0;
rep(i, 1, sz[u]) rep(j, 1, sz[v])
t[i] = (t[i] + f[u][i] * f[v][j] % mod * g[j] % mod * (-1) % mod + mod) % mod,
t[i + j] = (t[i + j] + f[u][i] * f[v][j] % mod) % mod;
sz[u] += sz[v]; rep(i, 1, sz[u]) f[u][i] = t[i];
第四行的转移:假如节点
第五行的转移:然后就统计将
最后,因为我们统计方案数的时候已经把容斥系数一并算进去了,所以最后直接乘上匹配数再相加即可:rep(i, 1, n) ans = (ans + g[i] * f[1][i] % mod) % mod;
。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
const int mod = 1e9 + 7, maxn = 5e3 + 5;
int n, u, v, ans, sz[maxn];
int f[maxn][maxn], g[maxn], t[maxn];
vector <int> e[maxn];
inline void tree_dp(int u, int fa){
f[u][sz[u] = 1] = 1;
for(auto v : e[u]) if(v ^ fa){
tree_dp(v, u); rep(i, 1, sz[u] + sz[v]) t[i] = 0;
rep(i, 1, sz[u]) rep(j, 1, sz[v])
t[i] = (t[i] + f[u][i] * f[v][j] % mod * g[j] % mod * (-1) % mod + mod) % mod,
t[i + j] = (t[i + j] + f[u][i] * f[v][j] % mod) % mod;
sz[u] += sz[v]; rep(i, 1, sz[u]) f[u][i] = t[i];
}
}
signed main(){
scanf("%lld", &n);
rep(i, 2, n) scanf("%lld%lld", &u, &v),
e[u].push_back(v), e[v].push_back(u);
g[0] = 1; rep(i, 2, n) if(!(i & 1)) g[i] = g[i - 2] * (i - 1) % mod;
tree_dp(1, 0); rep(i, 1, n) ans = (ans + g[i] * f[1][i] % mod) % mod;
return printf("%lld\n", ans), 0;
}
标签:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通