• 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);
    	}
    }
    
  • 相关阅读:
    file is universal (3 slices) but does not contain a(n) armv7s slice error for static libraries on iOS
    WebImageButton does not change images after being enabled in Javascript
    ajax OPTION
    编程遍历页面上所有TextBox控件并给它赋值为string.Empty?
    获取海洋天气预报
    C#线程系列教程(1):BeginInvoke和EndInvoke方法
    js控制只能输入数字和小数点
    Response.AddHeader(,)
    ManualResetEvent的理解
    Convert.ToInt32、int.Parse(Int32.Parse)、int.TryParse、(int) 区别
  • 原文地址:https://www.cnblogs.com/naruto-mzx/p/13128931.html
Copyright © 2020-2023  润新知