ARC101E - Ribbons on Tree

题目链接

ARC101E - Ribbons on Tree

题解

令边集\(S \subseteq E\)\(f(S)\)为边集S中没有边被染色的方案数
容斥一下,那么\(ans = \sum_{S \subseteq E} (-1)^{ \| S\| f(S) }\)
那么如何求对于原边集的\(f(S)\),也就是把\(S\)集合中的边全部删掉之后的各联通块内匹配的乘积
\(g(x)\)为大小为x的联通块内点两两匹配的方案
那么\(f(S)=\prod_{i=1}^{|S|+1}g(a_i)\)
考虑如何求ans
\(dp[i][j]\)表示以i为跟的子树中,有j各点没有在子树种匹配(链接到父节点
转移背包一下
对于\(j=0\)的时候由于那么i节点到父亲的边是没有覆盖的,容斥系数要取反
那么
$ f[i][0]=\sum_{j=1}^{sz[i]}-1\times f[i][j]\times g(j) $

代码


#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#define rep(p,x,k) for(int p = x;p <= k;++ p) 
#define per(p,x,k) for(int p = x;p >= k;-- p) 
#define gc getchar()
#define pc putchar 
#define LL long long  
#define int long long
inline LL read() {
    LL x = 0,f = 1; 
    char c = gc; 
    while(c < '0' || c > '9') c = gc; 
    while(c <= '9' && c >= '0') x = x * 10 + c -'0',c = gc; 
    return x ; 
} 
void print(LL x) { 
    if(x < 0) { 
        pc('-'); 
        x = -x; 
    } 
    if(x >= 10) print(x / 10); 
    pc(x % 10 + '0'); 
} 
const int maxn = 5007; 
const int mod = 1e9 + 7; 
int a[maxn]; 
int n; 
struct node { 
    int v,next; 
} edge[maxn << 1]; 
int num = 0,head[maxn]; 
inline void add_edge(int u,int v) {
    edge[++ num].v = v; edge[num].next = head[u];head[u] = num; 
} 
int g[maxn]; 
int dp[maxn][maxn]; 
int siz[maxn]; 
void dfs(int x,int fa) { 
    static int t[maxn]; 
    dp[x][1] = 1; siz[x] = 1; 
    for(int i = head[x];i;i = edge[i].next) { 
        int v = edge[i].v;  
        if(v == fa) continue; 
        dfs(v,x);   
        for(int j = 0,kel = siz[v];j <= siz[x];++ j) {
            for(int k = 0;k <= siz[v];++ k) { 
                (t[j + k] += 1ll * dp[x][j] * dp[v][k] % mod) %= mod; 
            } 
        } 
        siz[x] += siz[v]; 
        for(int j = 0;j <= siz[x];++ j) dp[x][j] = t[j],t[j] = 0; 
    } 
    LL sum = 0; 
    for(int i = 0;i <= siz[x];i += 2) sum += mod - 1ll * dp[x][i] * g[i] % mod;
    dp[x][0] = sum % mod; 
} 
 main() { 
    n = read(); 
    int u,v; 
    rep(i, 1,n - 1) { 
        u = read(),v = read(); 
        add_edge(u,v); 
        add_edge(v,u); 
    } 
    g[0] = 1; 
    for(int i = 2;i <= n;i += 2) (g[i] = 1ll * g[i - 2] * (i - 1)) %= mod;  
    dfs(1,1);  
    print((mod - dp[1][0]) % mod); 
    return 0; 
} 
/*
4
1 2
1 3
1 4 
*/

转载于:https://www.cnblogs.com/sssy/p/9800703.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值