zoukankan      html  css  js  c++  java
  • 【CF512D】Fox And Travelling(拓扑+树上背包)

    点此看题面

    • 给定一张(n)个点(m)条边的图。
    • 访问图中一个点,需要满足与它直接相连的所有点中至多只有一个没被访问过。
    • 对于每个(kin[0,n]),你需要求出访问(k)个点的方案数。
    • (nle100)

    拓扑去环

    首先我们考虑,环上的点是永远不可能被访问的,且两个环之间的路径上的点也是永远不可能被访问的(因为它们度数始终大于等于(2))。

    故我们完全可以从原图中先把所有环删去,而这一过程的实现可以用拓扑,因为环是不可能加入到拓扑序列中的。

    删去环后原图就变成了森林,但是其中的树分无根树和有根树两类:

    • 如果这本来就是一棵树,就是无根树
    • 如果这本来与一个环相连,就以与环相连的点为根。

    树上背包

    有根树的树上背包应该是很容易的吧,转移方程就是:

    [f_{x,j+k} exttt{+=}C_{j+k}^j imes f_{x,j} imes f_{y,k} ]

    这里因为两个序列自身是有序的,所以它们合并的方案数只要乘上一个组合数就好了。

    而对于无根树,我们以每个点为根都做一次树上背包。

    那么考虑选(i)个点的每种方案会被重复计算多少次(假设树大小为(tot)),发现若(i ot=tot),则只要不以这(i)个点为根都会把该方案计算一遍。

    也就是说,做无根树时,对于(i ot=tot),我们需要给最终的(f_i)除以(tot-i)

    最后不同树之间的合并又是一个背包,转移和上面给出的方程基本一致,就不再赘述了。

    代码:(O(n^3))

    #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 100
    #define X 1000000009
    using namespace std;
    int n,m,C[N+5][N+5];
    I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    struct Graph
    {
    	int ee,lnk[N+5];struct edge {int to,nxt;}e[N*N+5];
    	I int& operator [] (CI x) {return lnk[x];}
    	I edge& operator () (CI x) {return e[x];}
    	I void add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y;}
    }G,P;
    namespace Topo//拓扑
    {
    	int d[N+5],q[N+5],vis[N+5],IQ[N+5],rt[N+5],ty[N+5];
    	I void MakeRt(CI x,CI Rt)//以Rt为根(对于无根树,方便起见也给一个根)
    	{
    		rt[x]=Rt;for(RI i=P[x];i;i=P(i).nxt) !rt[P(i).to]&&(MakeRt(P(i).to,Rt),0);
    	}
    	I void Work()
    	{
    		RI i,H=1,T=0;for(i=1;i<=G.ee;++i) ++d[G(i).to];//初始化度数
    		for(i=1;i<=n;++i) d[i]<=1&&(IQ[q[++T]=i]=1);//初始化队列
    		W(H<=T) for(vis[q[H]]=1,i=G[q[H++]];i;i=G(i).nxt)//拓扑去环
    			!vis[G(i).to]&&--d[G(i).to]<=1&&!IQ[G(i).to]&&(IQ[q[++T]=G(i).to]=1);
    		RI x,f;for(x=1;x<=T;++x) if(d[q[x]])
    		{
    			for(f=1,i=G[q[x]];i;i=G(i).nxt) IQ[G(i).to]&&(P.add(q[x],G(i).to),P.add(G(i).to,q[x]),f=0);//连边
    			f&&(ty[q[x]]=1),IQ[q[x]]=0;//判断是否与环相连
    		}
    		for(x=1;x<=T;++x) ty[q[x]]&&(MakeRt(q[x],q[x]),0);for(x=1;x<=T;++x) !rt[q[x]]&&(MakeRt(q[x],q[x]),0);//优先建有根树,剩下的是无根树
    	}
    }
    namespace Bag//树上背包
    {
    	int tot,f[N+5][N+5],f_[N+5],g[N+5],F[N+5];
    	I void DP(CI x,CI lst=0)//树形DP
    	{
    		for(RI i=1;i<=g[x];++i) f[x][i]=0;f[x][g[x]=0]=1;
    		for(RI i=P[x],j,k;i;i=P(i).nxt) if(P(i).to^lst)//树上背包板子,但注意组合数
    		{
    			for(DP(P(i).to,x),j=g[x];~j;--j) for(k=1;k<=g[P(i).to];++k)
    				f[x][j+k]=(1LL*f[x][j]*f[P(i).to][k]%X*C[j+k][j]+f[x][j+k])%X;g[x]+=g[P(i).to];
    		}
    		++g[x],f[x][g[x]]=f[x][g[x]-1];//因为根只能放最后一个,可直接复制答案
    	}
    	I void Walk(CI x,CI lst=0)//以每个点为根做背包
    	{
    		RI i;for(i=P[x];i;i=P(i).nxt) P(i).to^lst&&(Walk(P(i).to,x),0);
    		for(DP(x),i=1;i<=g[x];++i) f_[i]=(f_[i]+f[x][i])%X;
    	}
    	I void Solve(CI x,CI ty)//求解x所在的树,ty表示是否有根
    	{
    		RI i,j;if(ty) for(DP(x),i=1;i<=g[x];++i) f_[i]=f[x][i];//有根树直接DP
    		else {for(i=1;i<=n;++i) f_[i]=0;for(Walk(x),i=1;i^g[x];++i) f_[i]=1LL*f_[i]*QP(g[x]-i,X-2)%X;}//无根树给f[i]除以g[x]-i
    		for(i=tot;~i;--i) for(j=1;j<=g[x];++j) F[i+j]=(1LL*F[i]*f_[j]%X*C[i+j][i]+F[i+j])%X;tot+=g[x];//总背包统计所有答案
    	}
    }
    int main()
    {
    	RI i,j,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),G.add(x,y),G.add(y,x);
    	for(C[0][0]=i=1;i<=n;++i) for(C[i][0]=j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%X;//预处理组合数
    	for(Topo::Work(),Bag::F[0]=i=1;i<=n;++i) i==Topo::rt[i]&&(Bag::Solve(i,Topo::ty[i]),0);//拓扑去环,然后分别处理每一棵树
    	for(i=0;i<=n;++i) printf("%d
    ",Bag::F[i]);return 0;//输出总背包中的答案
    }
    
  • 相关阅读:
    vue中的具名插槽
    vue中默认插槽slot
    局部组件使用指令-方法-过滤器-计算属性
    vue创建局部组件
    Class Metaprogramming
    Attribute Descriptors
    Dynamic Attributes and Properties
    Concurrency with asyncio
    Concurrency with Futures
    Coroutines
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF512D.html
Copyright © 2011-2022 走看看