SNOI2020 LOJ3323 生成樹
阿新 • • 發佈:2020-06-28
題目傳送門
分析:
樹上問題放仙人掌上考已經很離譜了,仙人掌上加一條邊是什麼爛玩意??
本題會反覆運用仙人掌的一個公式:
點-邊+環=1
我們先判斷一下\(G\)是否是一棵仙人掌,如果是就直接把環的大小乘起來就好了
如果不是,我們就要想辦法找到哪一條邊在作怪
先找點雙連通分量,肯定會形成若干個環和一個奇奇怪怪的點雙
單獨處理這一個奇怪的點雙,作為子圖\(G'\)處理
子圖\(G'\)大概會出現兩種情況,一種是一個大環上面串很多小環,另一種是兩個環共用一條邊(樣例),但其實兩者本質相同
胡亂分析一下,如果一條邊的一個端點度數為3,那麼這條邊就很有嫌疑
我們把所有這種邊放到一起,隨機排序(資料挺水可以不用2333)後,暴力刪每一條邊,檢驗剩下的圖是不是仙人掌
隨機之後找到某一條滿足條件的邊\(E\)
剩下的圖縮點之後加上\(E\),明顯是一個環,而環上每一條邊與\(E\)等效
一頓加加減減就好了。。。
剩下的情況就是縮點後的環並不刪邊,我們刪去本來被縮點的某一個小環上的兩條邊,也會形成生成樹
這種情況在縮點時順便處理一下
一頓加加減減就好了。。。
程式碼寫起來噁心,細節很多,講不清楚,不理解的看看程式碼吧。。。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<iostream> #include<map> #include<string> #define maxn 1000005 #define MOD 998244353 using namespace std; inline int getint() { int num=0,flag=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1; while(c>='0'&&c<='9')num=num*10+c-48,c=getchar(); return num*flag; } int n,m; struct node{int u,v;}e[maxn]; int fir[maxn],nxt[maxn],to[maxn],cnt=1; int dfn[maxn],low[maxn],tim,stk[maxn],tp; int Pd=1; vector<int>scc[maxn],scce[maxn],G,E,VE; int scccnt,fa[maxn],tot,T[maxn],P[maxn]; bool ban[maxn]; int d[maxn],Cnt,sum; inline int ksm(int num,int k) { int ret=1; for(;k;k>>=1,num=1ll*num*num%MOD)if(k&1)ret=1ll*ret*num%MOD; return ret; } inline void newnode(int u,int v) {to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt;} inline void tarjan(int u,int pre) { dfn[u]=low[u]=++tim;stk[++tp]=u; for(int i=fir[u];i;i=nxt[i])if(!ban[i>>1]&&i!=pre) { int v=to[i]; if(!dfn[v]) { tarjan(v,i^1),low[u]=min(low[u],low[v]); if(low[v]==dfn[u]) { int x;tot++,scccnt++,fa[tot]=u; do{x=stk[tp--],scc[scccnt].push_back(x),fa[x]=tot;}while(x!=v); scc[scccnt].push_back(u); } else if(low[v]>dfn[u])tp--,fa[v]=u; } else low[u]=min(low[u],dfn[v]); } } inline void dfs(int u,int pre) { dfn[u]=low[u]=++tim;stk[++tp]=u; for(int i=fir[u];i;i=nxt[i])if(!ban[i>>1]&&i!=pre) { int v=to[i]; if(!dfn[v]) { dfs(v,i^1),low[u]=min(low[u],low[v]); if(low[v]==dfn[u]) { int x;scccnt++; do{x=stk[tp--];}while(x!=v); } else if(low[v]>dfn[u])tp--; } else low[u]=min(low[u],dfn[v]); } } inline void solve(int u,int pre) { dfn[u]=low[u]=++tim;stk[++tp]=u; for(int i=fir[u];i;i=nxt[i])if(!ban[i>>1]&&i!=pre) { int v=to[i]; if(!dfn[v]) { solve(v,i^1),low[u]=min(low[u],low[v]); if(low[v]==dfn[u]) { int x;scccnt++; do{ x=stk[tp--],scc[scccnt].push_back(x); if(d[x]>2)P[scccnt]=scc[scccnt].size(); }while(x!=v); scc[scccnt].push_back(u); } else if(low[v]>dfn[u])tp--; } else low[u]=min(low[u],dfn[v]); } } int main() { tot=n=getint(),m=getint(); for(int i=1;i<=m;i++) { int u=e[i].u=getint(),v=e[i].v=getint(); newnode(u,v),newnode(v,u); } tarjan(1,0); for(int p=1;p<=n;p++)for(int i=fir[p];i;i=nxt[i])if(to[i]>p) { int u=p,v=to[i]; if(fa[fa[u]]==v)swap(u,v); if(fa[v]==fa[u]||fa[fa[v]]==u)T[fa[v]-n]++,scce[fa[v]-n].push_back(i>>1); } int p=0; for(int i=1;i<=scccnt;i++)if(T[i]!=scc[i].size()){p=i;break;} if(!p) { int ans=1; for(int i=1;i<=scccnt;i++)ans=1ll*ans*scc[i].size()%MOD; printf("%d\n",ans); return 0; } G=scc[p],E=scce[p]; for(int i=1;i<=scccnt;i++)if(i!=p)Pd=1ll*Pd*scc[i].size()%MOD; for(int i=1;i<=scccnt;i++)scc[i].clear(),scce[i].clear(); memset(fir,0,sizeof fir),cnt=1; for(int i=0;i<E.size();i++) newnode(e[E[i]].u,e[E[i]].v),newnode(e[E[i]].v,e[E[i]].u),d[e[E[i]].u]++,d[e[E[i]].v]++; for(int i=0;i<E.size();i++)if(d[e[E[i]].u]==3||d[e[E[i]].v]==3)VE.push_back(i+1); int ID=0; for(int t=0;t<VE.size();t++) { ban[VE[t]]=1;tim=tp=scccnt=0; for(int i=0;i<G.size();i++)dfn[G[i]]=0; dfs(G[0],0); ban[VE[t]]=0; if(G.size()-E.size()+scccnt==0){ID=t;break;} } ban[VE[ID]]=1;tim=tp=scccnt=0; for(int i=0;i<G.size();i++)dfn[G[i]]=0; solve(e[E[VE[ID]-1]].u,0); int ans=1,tmp=0; for(int i=1;i<=scccnt;i++)if(scc[i].size()>1)ans=1ll*ans*scc[i].size()%MOD,tmp+=scc[i].size(); sum=1ll*Pd*ans%MOD*(E.size()-tmp)%MOD; for(int i=1;i<=scccnt;i++)if(scc[i].size()>1) { int d1=P[i],d2=scc[i].size()-P[i]; sum=(sum+1ll*Pd*ans%MOD*d1%MOD*d2%MOD*ksm(scc[i].size(),MOD-2))%MOD; } printf("%d\n",sum); }