zoukankan      html  css  js  c++  java
  • BZOJ4787/UOJ290 【ZJOI2017】仙人掌

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000 
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

    题目链接:UOJ290

    正解:$DP$+仙人掌

    解题报告:

      考虑环上的边,不可能在连边中再被覆盖,所以只需要考虑树边就好了。

      把环拆掉,只剩下若干棵树,就是一个森林,最后把每棵树的答案用乘法原理合并起来就好了。

        对于每个节点$u$,我们考虑他的子树的连边方案数如何统计。

        如果我们强制每个结点的子树必须向外连一条边(显然最多一条),往上统计的话,

      那么假设$u$的子树内没有向外连边,那么就是把儿子节点的$ans$乘起来。

      如果向外连边了,就需要考虑互相连边的合法情况有多少种了。我们发现这个方案数只和儿子节点个数有关,可以很容易的用递推式来表示:

      $g[n]=g[n-1]+g[n-2]*(n-1)$

        预处理出$g$数组,每次对于每个节点先把儿子节点的$ans$全乘进来,接下来需要分类讨论节点$u$是不是一棵树的根。

      如果是根的话,则不能向外连边,那么再乘上儿子节点个数的$g$就好了(相当于是组合了节点个数个点的互相连边方式);

      否则,可以向外连边,那么把节点$u$本身也可以算进来,就是再乘上儿子节点个数$+1$的$g$。

    //It is made by ljh2000
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <string>
    #include <complex>
    #include <bitset>
    using namespace std;
    typedef long long LL;
    typedef long double LB;
    typedef complex<double> C;
    const double pi = acos(-1);
    const int MAXN = 500011;
    const int MAXM = 1000011; 
    const int mod = 998244353;
    int n,m,ecnt,first[MAXN],to[MAXM],next[MAXM],father[MAXN],lu[MAXN],dfn[MAXN],deep[MAXN];
    LL g[MAXN],f[MAXN],ans;
    struct node{ int id,x; }a[MAXN];
    inline bool cmp(node q,node qq){ return q.x<qq.x; }
    inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void dfs(int x,int fa){
    	dfn[x]=++ecnt;
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==fa) continue;
    		if(!dfn[v]) father[v]=x,deep[v]=deep[x]+1,dfs(v,x);
    	}
    }
    
    inline void dp(int x,bool rt){
    	lu[x]=-1; f[x]=1; int tot=0;//the number of son
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==father[x]) continue;
    		if(lu[v]!=1) continue;
    		tot++;
    		dp(v,0); f[x]*=f[v]; f[x]%=mod;
    	}
    	if(tot==0) return ;
    	if(!rt) f[x]*=g[tot+1],f[x]%=mod;
    	else f[x]*=g[tot],f[x]%=mod;
    }
     
    inline void work(){
    	g[0]=g[1]=1; for(int i=2;i<=500001;i++) g[i]=g[i-1]+g[i-2]*(i-1),g[i]%=mod;
    	int T=getint(); int x,y; bool ck;
    	while(T--) {
    		n=getint(); m=getint(); ecnt=1;
    		for(int i=1;i<=n;i++) first[i]=father[i]=dfn[i]=deep[i]=lu[i]=0;
    		for(int i=1;i<=m;i++) {
    			x=getint(); y=getint();
    			link(x,y); link(y,x);
    		}
    		ecnt=0; deep[1]=1; dfs(1,0); ck=true;
    		for(int i=1;i<=m;i++) {//统计lu数组(到根的路径条数),判断是否为仙人掌
    			x=to[i<<1]; y=to[i<<1|1]; if(dfn[x]<dfn[y]) swap(x,y);
    			while(x!=y) {
    				lu[x]++;
    				if(lu[x]>2) { ck=false; break; }
    				x=father[x];
    			}
    		}
    		if(!ck) { printf("0
    "); continue; }
    		for(int i=1;i<=n;i++) a[i].id=i,a[i].x=deep[i];
    		sort(a+1,a+n+1,cmp);
    		ans=1;
    		for(int i=1;i<=n;i++) {
    			x=a[i].id;
    			if(lu[x]!=-1) {
    				dp(x,1);
    				ans*=f[x]; ans%=mod;
    			}
    		}
    		printf("%lld
    ",ans);
    	}
    }
     
    int main()
    {
        work();
        return 0;
    }
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    

      

  • 相关阅读:
    追求一个人怎么这么难
    基于PHPstream扩展手动实现一个redis客户端
    常见final修饰类
    位运算
    HTTP Status 404
    Hibernate Junit 运行报错:org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
    根据银行卡号码获取银行卡归属行以及logo图标
    进程间通信IPC(InterProcess Communication)
    脏读、幻读、不可重复读和可重复读
    数据库锁知识总结
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6613829.html
Copyright © 2011-2022 走看看