bzoj4784 [Zjoi2017]仙人掌


Description

如果一個無自環無重邊無向連通圖的任意一條邊最多屬於一個簡單環,我們就稱之為仙人掌。所謂簡單環即不經過重復的結點的環。

現在九條可憐手上有一張無自環無重邊的無向連通圖,但是她覺得這張圖中的邊數太少了,所以她想要在圖上連上一些新的邊。同時為了方便的存儲這張無向圖,圖中的邊數又不能太多。經過權衡,她想要加邊后得到的圖為一棵仙人掌。不難發現合法的加邊方案有很多,可憐想要知道總共有多少不同的加邊方案。兩個加邊方案是不同的當且僅當一個方案中存在一條另一個方案中沒有的邊。

Input

多組數據,第一行輸入一個整數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

Output

對於每組數據,輸出一個整數表示方案數,當然方案數可能很大,請對998244353取模后輸出。

Sample Input

2
3 2
1 2
1 3
5 4
1 2
2 3
2 4
1 5

Sample Output

2
8
對於第一組樣例合法加邊的方案有 {}, {(2,3)},共 2 種。

 

正解:仙人掌$DP$

這題好難啊。。我看題解都看了好久才看懂。。

先給兩個博客:http://blog.csdn.net/akak__ii/article/details/65935711

ljh2000:http://www.cnblogs.com/ljh2000-jump/p/6613829.html

首先特判不是仙人掌的情況,只要判每個點到達根的路徑是否大於$2$條就行了。

然后我們可以先把環拆掉,也就是把環邊和對應的那個點與它父親斷開,因為環是不會對答案造成貢獻的。然后這個仙人掌就會變成一個森林。於是我們就成功地把仙人掌$DP$變成了樹形$DP$。我們單獨考慮每棵樹的答案,乘法原理一下就好。

然后就是對於每棵樹統計答案了。

對於一個點$x$,我們設$f[x]$表示$x$這棵子樹連邊形成仙人掌的方案數。我們發現,可以分為兩種情況:

1,$x$這棵子樹一定不與祖先連邊,這個是根的情況。

2,$x$這棵子樹可能與祖先連邊,這個是除了根以外其他點的情況。

對於第1種情況,我們把$x$所有的兒子$f[v]$都乘起來,並且我們計算一下$x$的兒子互相連邊的情況,再乘起來就行了。

對於$x$的兒子互相連邊的情況,我們可以找到一個規律。我們設$g[i]$表示$i$個兒子互相連邊的合法方案數,那么$g[i]=g[i-1]+(i-1)*g[i-2]$。

這是怎么來的呢?我們考慮一下,如果第$i$個點不與其他點連邊,那么方案數就是$g[i-1]$,否則,第$i$個點與第$j$個點連邊,那么第$j$個點肯定不能與其他點連邊,所以方案數是$g[i-2]$,總共有$i-1$種情況,所以$g[i]=g[i-1]+(i-1)*g[i-2]$。那么我們設$x$有$tot$個兒子,於是$f[x]=\prod f[v]*g[tot]$。

那么現在我們只要考慮第二種情況了。其實仔細想想,就是$x$的所有兒子$f[v]$相乘,再乘上$g[tot+1]$就行了。因為這就是$tot+1$個點互相連邊的情況。於是$f[x]=\prod f[v]*g[tot+1]$。

於是這道題我們就完美地解決了。

 

 1 //It is made by wfj_2048~
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <complex>
 5 #include <cstring>
 6 #include <cstdlib>
 7 #include <cstdio>
 8 #include <vector>
 9 #include <cmath>
10 #include <queue>
11 #include <stack>
12 #include <map>
13 #include <set>
14 #define rhl (998244353)
15 #define inf (1<<30)
16 #define M (1000010)
17 #define N (500010)
18 #define il inline
19 #define RG register
20 #define ll long long
21 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
22 
23 using namespace std;
24 
25 struct edge{ int nt,to; }G[2*M];
26 struct node{ int i,d; }a[N];
27 
28 int head[N],fa[N],dfn[N],dep[N],lu[N],n,m,cnt;
29 ll f[N],g[N],ans;
30 
31 il int gi(){
32     RG int x=0,q=1; RG char ch=getchar();
33     while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
34     if (ch=='-') q=-1,ch=getchar();
35     while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
36     return q*x;
37 }
38 
39 il void insert(RG int from,RG int to){
40     G[++cnt]=(edge){head[from],to},head[from]=cnt; return;
41 }
42 
43 il int cmpd(const node &a,const node &b){ return a.d<b.d; }
44 
45 il void pre(){ //預處理g數組
46     g[0]=g[1]=1;
47     for (RG int i=2;i<=500000;++i) g[i]=(g[i-1]+(i-1)*g[i-2])%rhl;
48     return;
49 }
50 
51 il void dfs(RG int x,RG int p){
52     fa[x]=p,dfn[x]=++cnt,dep[x]=dep[p]+1;
53     for (RG int i=head[x],v;i;i=G[i].nt){
54     v=G[i].to; if (dfn[v]) continue;
55     dfs(v,x);
56     }
57     return;
58 }
59 
60 il void dp(RG int x,RG int rt){
61     lu[x]=-1,f[x]=1; RG int tot=0,v;
62     for (RG int i=head[x];i;i=G[i].nt){
63     v=G[i].to; if (v==fa[x] || lu[v]!=1) continue;
64     tot++; dp(v,0); f[x]=f[x]*f[v]%rhl;
65     }
66     if (!rt) f[x]=f[x]*g[tot+1]%rhl;
67     else f[x]=f[x]*g[tot]%rhl;
68     return;
69 }
70 
71 il void work(){
72     n=gi(),m=gi(),cnt=1;
73     for (RG int i=1;i<=n;++i) lu[i]=fa[i]=dep[i]=dfn[i]=head[i]=0;
74     for (RG int i=1,u,v;i<=m;++i) u=gi(),v=gi(),insert(u,v),insert(v,u);
75     cnt=0; dfs(1,0);
76     for (RG int i=1,u,v;i<=m;++i){ //統計每個點到根的路徑數
77     u=G[i<<1].to,v=G[i<<1|1].to;
78     if (dfn[u]<dfn[v]) swap(u,v);
79     while (u!=v){
80         if (lu[u]==2){ printf("0\n"); return; }
81         lu[u]++,u=fa[u];
82     }
83     }
84     for (RG int i=1;i<=n;++i) a[i].i=i,a[i].d=dep[i];
85     sort(a+1,a+n+1,cmpd); ans=1;
86     for (RG int i=1,x;i<=n;++i){
87     x=a[i].i; if (lu[x]==-1) continue;
88     dp(x,1); ans=ans*f[x]%rhl;
89     }
90     printf("%lld\n",ans); return;
91 }
92 
93 int main(){
94     File("cactus");
95     pre(); RG int T=gi();
96     while (T--) work();
97     return 0;
98 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM