• 【AT4352】[ARC101C] Ribbons on Tree(容斥+DP)


    点此看题面

    • 给定一棵(n)个点的树((n)为偶数),要求将点两两配对,并给每对点间路径上的边染色。
    • 求有多少种配对方案使得所有边都被染色。
    • (nle5000)

    容斥

    这种所有边都染色的问题很容易想到容斥。

    即设至少(k)条边未被染色,就将这部分答案乘上容斥系数((-1)^k)计入最终答案中。

    而一条边未被染色,说明不能跨过这条边给点配对。也就是说这(k)条边将把整棵树分成若干连通块,而所有的配对都只能在每个连通块内部进行。

    为此,我们设出(h_i)表示将一个大小为(i)的连通块中的点两两配对的方案数,只要考虑与某一个点配对的点是谁,就能得出递推式:(h_i=h_{i-2} imes(i-1))

    动态规划

    (f_{x,i})表示执行到(x)的子树内,(x)所在连通块大小为(i)时的方案数,这里已经乘上了容斥系数。

    从一个子节点状态(f_{y,j})转移时,考虑这条边是否断开,分两种转移:

    [tmp_{x,i+j} exttt{+=}f_{x,i} imes f_{y,j}\ tmp_{x,i} exttt{+=}(-1) imes h_j imes f_{x,i} imes f_{y,j} ]

    由于每个点第二维上界是子树大小,可以用树上背包的记(size)优化保证复杂度。

    代码:(O(n^2))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 5000
    #define X 1000000007
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,h[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
    int f[N+5][N+5],g[N+5],tmp[N+5];I void DP(CI x,CI lst=0)//树形DP
    {
    	f[x][g[x]=1]=1;for(RI i=lnk[x],j,k;i;i=e[i].nxt) if(e[i].to^lst)
    	{
    		for(DP(e[i].to,x),j=1;j<=g[x];++j) for(k=1;k<=g[e[i].to];++k)//g[x]记size优化
    			tmp[j+k]=(1LL*f[x][j]*f[e[i].to][k]+tmp[j+k])%X,//不断这条边
    			tmp[j]=((X-1LL)*h[k]%X*f[x][j]%X*f[e[i].to][k]+tmp[j])%X;//断开这条边
    		for(g[x]+=g[e[i].to],j=1;j<=g[x];++j) f[x][j]=tmp[j],tmp[j]=0;//把临时数组中的值转移到f中
    	}
    }
    int main()
    {
    	RI i,x,y;for(scanf("%d",&n),i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    	for(h[0]=1,i=2;i<=n;i+=2) h[i]=1LL*h[i-2]*(i-1)%X;//预处理每种大小连通块两两配对方案
    	RI t=0;for(DP(1),i=1;i<=n;++i) t=(1LL*h[i]*f[1][i]+t)%X;return printf("%d
    ",t),0;//最后还要算上1号点所在连通块方案
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    LeetCode 面试题32
    LeetCode 102. 二叉树的层序遍历
    LeetCode 面试题32
    LeetCode 面试题32
    LeetCode 面试题31. 栈的压入、弹出序列
    LeetCode 946. 验证栈序列
    LeetCode 50. Pow(x, n)
    LeetCode 572. 另一个树的子树
    LeetCode 面试题50. 第一个只出现一次的字符
    LeetCode 面试题37. 序列化二叉树
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT4352.html
Copyright © 2020-2023  润新知