• luogu P3687 [ZJOI2017]仙人掌 |树形dp


    题目描述

    如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌。所谓简单环即不经过重复的结点的环。

    现在九条可怜手上有一张无自环无重边的无向连通图,但是她觉得这张图中的边数太少了,所以她想要在图上连上一些新的边。同时为了方便的存储这张无向图,图中的边数又不能太多。经过权衡,她想要加边后得到的图为一棵仙人掌。

    不难发现合法的加边方案有很多,可怜想要知道总共有多少不同的加边方案。

    两个加边方案是不同的当且仅当一个方案中存在一条另一个方案中没有的边。

    输入格式

    多组数据,第一行输入一个整数 (T) 表示数据组数。

    每组数据第一行输入两个整数 (n),(m),表示图中的点数与边数。

    接下来 m 行,每行两个整数 (u),(v)(1 ≤ (u),(v)(n),(u)(v)) 表示图中的一条边。保证输入的图联通且没有自环与重边。

    输出格式

    对于每组数据,输出一个整数表示方案数,当然方案数可能很大,请对 998244353 取模后输出。


    用f[i]表示子树i的方案数,g[i]表示可向上拓展的子树的方案数,num表示子树的节点个数(即大小)

    (f[i]=h[num]*prod g[son](sonin Child(i)))

    每一个子节点都可以向上扩展并相对独立,然后一共有h[num]种儿子的匹配方案

    (g[i]=f[i]+h[num-1]*num*prod g[son](sonin Child(i)))

    该节点可以自己想上扩展为f[i],并且有num个子节点,每个子节点还可以选择一个儿子,并且一共有h[num-1]种匹配方案


    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N=5e5+5,mod=998244353;
    inline int read(){
        int x=0,f=1; char ch=getchar();
        while(ch>'9'||ch<'0'){ if(ch=='-')f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
        return x*f;
    }
    int nxt[N<<1],head[N],go[N<<1],flag[N<<1],tot;
    struct node{
    	int u,v;
    }e[N<<1];
    inline void add(int u,int v){
    	nxt[++tot]=head[u],head[u]=tot,go[tot]=v; e[tot]=(node){u,v}; flag[tot]=1;
    	nxt[++tot]=head[v],head[v]=tot,go[tot]=u; e[tot]=(node){u,v}; flag[tot]=1;
    }
    int dfn[N],low[N],st[N],co[N],top,num,col;
    #define ll long long
    bool isca;
    void Tarjan(int u,int fa){
    	bool flag=0;
    	dfn[u]=low[u]=++num;
    	st[++top]=u;
    	for(int i=head[u];i;i=nxt[i]){
    		int v=go[i];
    		if(v==fa)continue;
    		if(!dfn[v]){
    			Tarjan(v,u);
    			low[u]=min(low[u],low[v]);
    			if(low[v]<dfn[u]){
    				if(flag){ isca=0; return; }
    				flag=1;
    			}
    		}else {
    			low[u]=min(low[u],dfn[v]);
    			if(dfn[v]<dfn[u]){
    				if(flag){ isca=0; return; }
    				flag=1;
    			}
    		}
    	}
    	if(low[u]==dfn[u]){
    		co[u]=++col;
    		while(st[top]!=u){
    			co[st[top]]=col;
    			--top;
    		}
    		--top;
    	}
    }
    ll h[N],f[N],g[N];
    bool vis[N];
    void dfs(int u,int fa){
    	vis[u]=1,f[u]=1,g[u]=0;
    	int sum=0;
    	for(int i=head[u];i;i=nxt[i]){
    		int v=go[i];
    		if(!flag[i]||v==fa)continue;
    		dfs(v,u);
    		f[u]=f[u]*g[v]%mod;
    		sum++;
    	}
    	g[u]=(f[u]*h[sum]%mod+f[u]*h[sum-1]%mod*sum%mod)%mod;
    	f[u]=f[u]*h[sum]%mod;
    }
    
    signed main(){
    	h[0]=h[1]=1;
    	for(int i=2;i<=N-5;i++)h[i]=(h[i-1]+(h[i-2]*(i-1)%mod))%mod;
    	for(int T=read();T;T--){
    		int n=read(),m=read();
    		isca=1; tot=top=col=num=0; 
    		for(int i=1;i<=n;i++)head[i]=co[i]=dfn[i]=low[i]=vis[i]=0;
    		for(int i=1;i<=m;i++)add(read(),read());
    		Tarjan(1,0);
    		for(int i=1;i<=tot;i++)if(co[e[i].u]==co[e[i].v])flag[i]=0;
    		if(!isca){ printf("0
    "); continue; }
    		ll ans=1;
    		for(int i=1;i<=n;i++){
    			if(vis[i])continue;
    			dfs(i,0);
    			ans=ans*f[i]%mod;	
    		}
    		for(int i=1;i<=tot;i++)nxt[i]=0;
    		printf("%lld
    ",ans);
    	}
    }
    
  • 相关阅读:
    2015年蓝桥杯省赛A组c++第3题
    2015年蓝桥杯省赛A组c++第1题
    算法学竞赛常用头文件模板
    Android+Tomcat通过http获取本机服务器资源
    Ubuntu16.04LTS卸载软件的命令
    20个有趣的Linux命令
    Ubuntu16.04开机蓝屏问题解决
    Ubuntu启动时a start job is running for dev-disk-by延时解决
    Cookie与Session
    JS 闭包
  • 原文地址:https://www.cnblogs.com/naruto-mzx/p/13128931.html
Copyright © 2020-2023  润新知