zoukankan      html  css  js  c++  java
  • ●洛谷P3687 [ZJOI2017]仙人掌

    题链:

    https://www.luogu.org/problemnew/show/P3687
    题解:

    计数DP,树形DP。


    (首先对于这个图来说,如果初始就不是仙人掌,那么就直接输出0)
    然后由于本来图中就存在于环中的边,不可能再次被包含,
    所以图中的环就把这个图分为的若干颗树。
    那么答案就是分别求出每颗树的方案数并相乘。
    现在问题变为了求:把一颗树通过连边使得仍然是仙人掌的方案数。
    定义如下3个数组:
    f[u]:表示u这颗子树中没有一条从u到子树内某个的节点的路径可以向其它子树连边的方案数。
    g[u]:表示u这颗子树中有一条从u到子树内某个节点的路径可以其它子树连边的方案数。
    以及一个预处理的h[n]:
    表示有n个元素,每个元素可以选择另一个元素与其两两匹配或者该元素不与任何元素匹配的方案数。
    先看看h[n]的求法:
    h[n]=h[n-1]+h[n-2]*(n-1)
    即表明第n个元素要么独立,要么与另外(n-1)个元素中的某一个匹配。
    然后看看怎样DP:
    对于当前的子树的根u,v是其儿子节点,num是其儿子节点个数
    $$f[u]=sum g[v] imes h[num] $$
    因为num个儿子有h[num]种匹配方案数,所以乘上h[num]
    (所谓的匹配就是把g[v]中的那条路径的末端和与它匹配的那个v'的g[v']中的那条路径的末端连边)

    然后还要求出当前的g[u]:
    $$g[u]=f[u]+sum g[v] imes h[num-1] imes num$$
    含义如下:
    由于u点自己可以成为g[u]中的那条路径的末端,所以$$+f[u]$$
    然后u还可以选择一个儿子g[son]中的路径来连上自己形成新的g[u]中的路径。
    这样的话,方案数是 $g[son]* sum_{v!=son} g[v] imes h[num-1] = sum g[v] imes h[num-1]$
    又因为u有num个儿子,即有num中选法,所以:
    $$+sum g[v] imes h[num-1] imes num $$

    代码:

    #include<bits/stdc++.h>
    #define MAXN 500005
    #define MOD 998244353
    #define rint register int
    using namespace std;
    int Case,N,M,dnt,cactus,ANS;
    int dfn[MAXN],lu[MAXN],fa[MAXN],f[MAXN],g[MAXN],h[MAXN];
    struct node{
    	int id,odr;
    	bool operator < (const node &rtm) const{
    		return odr<rtm.odr;
    	}
    }A[MAXN];
    struct Edge{
    	int ent;
    	int to[MAXN*4],nxt[MAXN*4],head[MAXN];
    	void Reset(int n){
    		for(rint i=1;i<=n;i++) head[i]=0; ent=2;
    	}
    	void Adde(int u,int v){
    		to[ent]=v; nxt[ent]=head[u]; head[u]=ent++;
    	}
    }E;
    void dfs(int u,int dad){
    	dfn[u]=++dnt; fa[u]=dad;
    	for(int e=E.head[u];e;e=E.nxt[e]){
    		int v=E.to[e];
    		if(dfn[v]) continue;
    		dfs(v,u);
    	}
    }
    void DP(int u,int rt){
    	lu[u]=-1; f[u]=1; int num=0,gson=1;
    	for(int e=E.head[u];e;e=E.nxt[e]){
    		int v=E.to[e];
    		if(lu[v]!=1||v==fa[u]) continue;
    		DP(v,0); num++;
    		gson=1ll*gson*g[v]%MOD;
    	}
    	f[u]=1ll*gson*h[num]%MOD;
    	g[u]=(1ll*f[u]+1ll*gson*h[num-1]%MOD*num)%MOD;
    }
    int main(){
    	h[0]=h[1]=1;
    	for(int i=1;i<=500000;i++) h[i]=(1ll*h[i-1]+1ll*h[i-2]*(i-1))%MOD;
    	for(scanf("%d",&Case);Case;Case--){
    		scanf("%d%d",&N,&M);
    		E.Reset(N); dnt=0; cactus=1; ANS=1;
    		for(rint i=1;i<=N;i++) lu[i]=dfn[i]=fa[i]=0;
    		for(int i=1,a,b;i<=M;i++)
    			scanf("%d%d",&a,&b),E.Adde(a,b),E.Adde(b,a);
    		dfs(1,0);
    		for(int e=1,u,v;e<=M;e++){
    			u=E.to[e*2]; v=E.to[e*2+1];
    			if(dfn[u]>dfn[v]) swap(u,v);
    			while(v!=u){
    				lu[v]++;
    				if(lu[v]>2){cactus=0;break;}
    				v=fa[v];
    			}
    		}
    		if(!cactus){printf("%d
    ",0); continue;}
    		for(int i=1;i<=N;i++) A[i]=(node){i,dfn[i]};
    		sort(A+1,A+N+1);
    		for(int i=1,u;i<=N;i++){
    			u=A[i].id;
    			if(lu[u]==-1) continue;
    			DP(u,1); ANS=1ll*ANS*f[u]%MOD; 
    		}
    		printf("%d
    ",ANS);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    struts2在result中使用el表达式碰到的问题
    JSP学习笔记—— jsp中include文件指令乱码的三种解决方案
    SSH整合,applicationContext.xml中配置hibernate映射文件问题
    struts上传文件失败 ContentType not allowed错误解决方法【转】
    mysql5 乱码问题解决方案
    java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory的解决
    JQuery使用on绑定动态生成元素时碰到的问题
    Oracle异常处理
    C#窗口拦截键盘事件
    Oracle中动态SQL详解
  • 原文地址:https://www.cnblogs.com/zj75211/p/8541648.html
Copyright © 2011-2022 走看看