題目描述
給定無向連通圖 ,已知 在刪掉一條邊後是一顆仙人掌(仙人掌:不存在兩個擁有公共邊的簡單環的無向聯通圖),求 的生成樹個數。結果對 取模。
題目分析
如果就是原圖是仙人掌那麼答案顯然是所有環大小之積。
刪掉一條邊後是仙人掌,那麼 一定是這樣的形式:
就是一個大環串起了一些仙人掌串,沒有被大環串起的對答案的貢獻就是乘上環大小。
而且其中必然存在至少一條不在小環中的邊(只有兩個環貼在一起的情況特殊考慮,實現相同)
就是上圖中的紅色邊,記這樣的邊的條數爲 ,記小環的大小爲 ,小環兩半邊大小分別爲
那麼形成生成樹要麼是斷一條紅邊,所有環斷一條邊,要麼是斷兩條不在同一半的環邊,其它環斷一條邊。答案爲
那麼問題就是要求出 和 以及每個環某一半的大小。
先tarjan一遍求出這個大環對應的點雙,將其拿出來建新圖(除去不相關的點和邊)
這個大環一定滿足 邊數 > 點數,tarjan求點雙的邊數可以通過在棧中加入邊解決。
然後斷開一條紅色邊,使其形成一個仙人掌,觀察可以發現度數爲3的點一定有一條是紅色邊,所以枚舉某個度數爲3的點的所有邊斷開檢驗是否是仙人掌即可。
在得到仙人掌的過程中就可以記錄得到的點雙大小,某一半的大小可以通過記錄dfs深度相減得到,因爲環上只有進入和出去的點的度數>2,可以藉此求解。
想清楚了還是蠻好做的。
Code:
#include<bits/stdc++.h>
#define maxn 500005
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const int mod = 998244353;
int n,m,prd=1,inv[maxn],dis[maxn],X[maxn],Y[maxn],deg[maxn];
int dep[maxn],dfn[maxn],low[maxn],stk[maxn*2],top,tim,sz;
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot=1;
vector<int>P,Q[maxn]; int id;
void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
bool ban[maxn],onc[maxn];
void tarjan(int u,int ff){
dfn[u]=low[u]=++tim,stk[++top]=u;
for(int i=fir[u],v;i;i=nxt[i]) if(!ban[i/2]&&i^1^ff){
if(!dfn[v=to[i]]){
stk[++top]=-i,dep[v]=dep[u]+1,tarjan(v,i),low[u]=min(low[u],low[v]);
if(dfn[u]==low[v]){
Q[++sz]=vector<int>(1,u); int edg=0;
for(int t=0;t!=-i;){
if((t=stk[top--])<0) edg++;
else {Q[sz].push_back(t); if(deg[t]>2) dis[sz]=dep[t]-dep[u];}
}
if(edg>Q[sz].size()) id=sz;
}
else if(dfn[u]<low[v]) top-=2;
}
else if(dfn[u]>dfn[v]) stk[++top]=-i,low[u]=min(low[u],dfn[v]);
}
}
int main()
{
read(n),read(m);
inv[0]=inv[1]=1; for(int i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<=m;i++) read(X[i]),read(Y[i]),line(X[i],Y[i]),line(Y[i],X[i]);
tarjan(1,0);
for(int i=1;i<=sz;i++) if(i!=id) prd=1ll*prd*Q[i].size()%mod;
if(!id) return printf("%d\n",prd),0;
P=Q[id];
memset(fir,0,(n+1)<<2),tot=1;
for(int i=P.size()-1;i>=0;i--) onc[P[i]]=1;
int M=0;
for(int i=1;i<=m;i++) if(onc[X[i]]&&onc[Y[i]]) M++,deg[X[i]]++,deg[Y[i]]++,line(X[i],Y[i]),line(Y[i],X[i]);
for(int k=P.size()-1,u;k>=0;k--) if(deg[u=P[k]]==3)
for(int j=fir[u];j;j=nxt[j]){
ban[j/2]=1;
memset(dfn,0,(n+1)<<2),dep[u]=0,top=tim=sz=id=0;
tarjan(u,0);
if(id) {ban[j/2]=0; continue;}
int cnt=0,pw=1,sum=0;
for(int i=1,siz;i<=sz;i++){
siz=Q[i].size(),pw=1ll*pw*siz%mod,cnt+=siz;
sum=(sum+1ll*dis[i]*(siz-dis[i])%mod*inv[siz])%mod;
}
return printf("%d\n",1ll*(sum+M-cnt)*pw%mod*prd%mod),0;
}
}