loj2250/bzoj4784/洛谷P3687 仙人掌 DP
阿新 • • 發佈:2018-11-08
題目分析
如果原圖不是一個仙人掌,答案就是0.
對於一個環,環上的兩個點,若分別連著不是該環上的點,點集為 和 ,那麼 和 之間不能連邊。所以我們可以去掉所有環上的邊,原圖就變成了一個森林,對於每棵樹單獨考慮。
由於題目中的仙人掌要求沒有重邊,所以我們可以認為每一條樹邊都要被一條非樹邊覆蓋,如果一條非樹邊只覆蓋一條樹邊,則認為在連出來的仙人掌上這條樹邊沒有被覆蓋。顯然不影響方案數。
對於一棵樹,考慮一個點相鄰的連通塊之間是怎麼覆蓋的,就會發現貢獻之和度數有關。對於點 ,由於它與兒子之間的邊要被非樹邊覆蓋(所謂兒子就是相鄰點),所以要麼兩棵子樹互相連邊,要麼一棵子樹裡連一條邊到 上。我先欽定 的一個兒子,根據這個子樹怎麼連來DP,那麼度數為 的點,子樹之間連邊的方案數就是
將所有點的貢獻乘起來即可。
程式碼
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int mod=998244353,N=500005,M=2000005;
int T,n,m,tot,ans,tim;
int h[N],ne[M],to[M],g[N],du[N],inc[N],dfn[N],pre[N];
int qm(int x) {return x>=mod?x-mod:x;}
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
int dfs(int x,int las) {
dfn[x]=++tim;
for(RI i=h[x];i;i=ne[i]) {
if(to[i]==las) continue;
if(!dfn[to[i]]) {
if(!dfs(to[i],x)) return 0;
pre[to[i]]=x;
}
else if(dfn[to[i]]>dfn[x]) {
int y=to[i];
while(y!=x) {
--du[y],--du[pre[y]];
if(inc[y]) return 0;
inc[y]=1,y=pre[y];
}
--du[x],--du[to[i]];
}
}
return 1;
}
int main()
{
int x,y;
T=read();
g[0]=g[1]=1;
for(RI i=2;i<=500000;++i) g[i]=qm(g[i-1]+1LL*(i-1)*g[i-2]%mod);
while(T--) {
n=read(),m=read();
tot=tim=0,ans=1;
for(RI i=1;i<=n;++i) du[i]=h[i]=inc[i]=dfn[i]=0;
for(RI i=1;i<=m;++i)
x=read(),y=read(),add(x,y),add(y,x),++du[x],++du[y];
if(!dfs(1,0)) {puts("0");continue;}
for(RI i=1;i<=n;++i) ans=1LL*ans*g[du[i]]%mod;
printf("%d\n",ans);
}
return 0;
}