LOJ#3323. 「SNOI2020」生成樹

題目描述

給定無向連通圖 GG,已知 GG 在刪掉一條邊後是一顆仙人掌(仙人掌:不存在兩個擁有公共邊的簡單環的無向聯通圖),求 GG 的生成樹個數。結果對 998244353998244353 取模。
1n,m51051\le n,m\le5*10^5

題目分析

如果就是原圖是仙人掌那麼答案顯然是所有環大小之積。

GG 刪掉一條邊後是仙人掌,那麼 GG 一定是這樣的形式:

在這裏插入圖片描述
就是一個大環串起了一些仙人掌串,沒有被大環串起的對答案的貢獻就是乘上環大小。
而且其中必然存在至少一條不在小環中的邊(只有兩個環貼在一起的情況特殊考慮,實現相同)
就是上圖中的紅色邊,記這樣的邊的條數爲 cntcnt,記小環的大小爲 sizisiz_i,小環兩半邊大小分別爲 li,ril_i,r_i

那麼形成生成樹要麼是斷一條紅邊,所有環斷一條邊,要麼是斷兩條不在同一半的環邊,其它環斷一條邊。答案爲 cnt(sizi)+(lirisizi)(sizi)cnt*(\prod siz_i)+(\sum {l_i*r_i\over siz_i})*(\prod siz_i)

那麼問題就是要求出 cntcntsizisiz_i 以及每個環某一半的大小。

先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;
		}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章