bzoj4784【zjoi2017】仙人掌

题目描述

如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌。所谓简单环即不经过
重复的结点的环。
现在九条可怜手上有一张无自环无重边的无向连通图,但是她觉得这张图中的边数太少了,所以她想要在图上连上
一些新的边。同时为了方便的存储这张无向图,图中的边数又不能太多。经过权衡,她想要加边后得到的图为一棵
仙人掌。不难发现合法的加边方案有很多,可怜想要知道总共有多少不同的加边方案。两个加边方案是不同的当且
仅当一个方案中存在一条另一个方案中没有的边。

输入格式

多组数据,第一行输入一个整数T表示数据组数。
每组数据第一行输入两个整数n,m,表示图中的点数与边数。
接下来m行,每行两个整数u,v(1≤u,v≤n,u!=v)表示图中的一条边。保证输入的图
联通且没有自环与重边
Sigma(n)<=5*10^5,m<=10^6,1<=m<=n*(n-1)/2

输出格式

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

    • 由于环上的边无法再被另外的环覆盖,所以把所有的环拆掉得到森林;
    • 计算每颗树的$ans$乘起来;
    • $f[u]$表示以$u$为根的子树的方案,$g[u]$表示以$u$为根的子树并且还有某个点可以向上连边的方案;
    • 由于根也可以向上连,$g[u]$是包含$f[u]$的;
    • $f[u]$的递推可以将所有的儿子$v$的$g[v]$乘起来,在乘以儿子之间的互相连边或和$u$连边的方案数;
    • $h[i]$表示$i$个儿子时互相连边的方案:
    • $h[i] = h[i-1] + h[i-2]*(i-1)$;
    • $tot$表示$u$的儿子的个数:
    • $f[u] = \Pi_{v}g[v] * h[tot]$;
    • $u$的子树向上连边可以由$u$或者$u$的一个儿子$v$的子树向上连边;
    • $g[u] = f[u] + tot * \Pi_{v}g[v] h[tot-1] = \Pi_{v}g[v]*h[tot+1]$;
    • 我一直在纠结不连边的方案去哪了?其实不连边的方案数在统计$v$向上连到$u$时被统计了;
  •  1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=1000010,mod=998244353;
     4 char gc(){
     5     static char*p1,*p2,s[1000000];
     6     if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
     7     return(p1==p2)?EOF:*p1++; 
     8 }
     9 int rd(){
    10     int x=0;char c=gc();
    11     while(c<'0'||c>'9')c=gc();
    12     while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
    13     return x;
    14 }
    15 int T,n,m,vis[N],bl[N],dfn[N],low[N],idx,fg,ans,st[N],top,o,hd[N],cnt,f[N],g[N],h[N],d[N];
    16 struct Edge{int v,nt;}E[N<<1];
    17 inline void adde(int u,int v){
    18     E[o]=(Edge){v,hd[u]};hd[u]=o++;
    19     E[o]=(Edge){u,hd[v]};hd[v]=o++;
    20 }
    21 void tarjan(int u,int fa){
    22     if(fg)return;
    23     dfn[st[++top]=u]=low[u]=++idx;
    24     int tot=0;
    25     for(int i=hd[u],v;i;i=E[i].nt){
    26         v=E[i].v;
    27         if(v==fa)continue;
    28         if(dfn[v=E[i].v]){
    29             if(d[v])continue;
    30             if(dfn[v]<dfn[u]&&tot++){fg=1;break;}
    31             low[u]=min(low[u],dfn[v]);
    32         }else{
    33             tarjan(v,u);
    34             if(low[v]<dfn[u]&&tot++){fg=1;break;}
    35             low[u]=min(low[u],low[v]);
    36         }
    37     }
    38     if(dfn[u]==low[u]){
    39         int v;++cnt;
    40         do{bl[v=st[top--]]=cnt;d[v]=1;}while(v!=u);
    41     }
    42 }
    43 void dfs(int u){
    44     f[u]=g[u]=vis[u]=1;
    45     int tot=0;
    46     for(int i=hd[u];i;i=E[i].nt){
    47         int v=E[i].v;
    48         if(vis[v]||bl[v]==bl[u])continue;
    49         tot++;
    50         dfs(v);
    51         f[u]=1ll*f[u]*g[v]%mod;
    52         g[u]=1ll*g[u]*g[v]%mod;
    53     }
    54     f[u]=1ll*f[u]*h[tot]%mod; 
    55     g[u]=1ll*g[u]*h[tot+1]%mod;
    56 }
    57 int main(){
    58     #ifndef ONLINE_JUDGE
    59     freopen("T1.in","r",stdin);
    60     freopen("T1.out","w",stdout);
    61     #endif
    62     T=rd();
    63     h[0]=h[1]=1;
    64     for(int i=2;i<=500001;++i)h[i]=(h[i-1]+1ll*(i-1)*h[i-2]%mod)%mod;
    65     while(T--){
    66         n=rd();m=rd();
    67         fg=idx=cnt=top=0;ans=o=1;
    68         for(int i=1;i<=n;++i)vis[i]=hd[i]=dfn[i]=d[i]=low[i]=0;
    69         for(int i=1;i<=m;++i)adde(rd(),rd());
    70         tarjan(1,0);
    71         if(fg){puts("0");continue;}
    72         for(int i=1;i<=n;++i)if(!vis[i]){
    73             dfs(i);
    74             ans = 1ll * ans * f[i]%mod;
    75         }
    76         printf("%d\n",ans);
    77     }
    78     return 0;
    79 }
    bzoj4784

     

 

posted @ 2019-01-22 22:07  大米饼  阅读(201)  评论(0编辑  收藏  举报