1. 程式人生 > >UOJ#23. 【UR #1】跳蚤國王下江南 仙人掌 Tarjan 點雙 圓方樹 點分治 多項式 FFT

UOJ#23. 【UR #1】跳蚤國王下江南 仙人掌 Tarjan 點雙 圓方樹 點分治 多項式 FFT

原文連結https://www.cnblogs.com/zhouzhendong/p/UOJ23.html

題目傳送門 - UOJ#23

題意

  給定一個有 n 個節點的仙人掌(可能有重邊)。

  對於所有的 $L(1\leq L\leq n-1)$ ,求出有多少不同的從節點 1 出發的包含 L 條邊的簡單路徑。簡單路徑是指不重複經過任意一點。

  $n\leq 10^5$

題解

  首先我們把走一條邊看作多項式 $x^1$ ,那麼一條長度為 L 的路徑就是其路徑上的多項式的乘積。

  接下來稱“環根”為距離節點 1 最近的那個節點。

  假設 $p_v$ 為點 v 對最終答案的貢獻,那麼 $p_v$ 是什麼?分兩種情況討論:

    1. v 屬於某個環且 v 不是該環的環根。那麼顯然這個環的環根(設為 u)更靠近 1 ,假設 v 到 u 的兩條路徑長度分別是 a 和 b ,那麼 $p_v=p_u \times (x^a+x^b)$ 。

    2. 假設有一個點 u 不和 v 在同一個環中,且 u 距離 1 比 v 距離 1 更近,那麼 $p_v=p_v\times x$ 。

 

  於是到此為止我們已經可以輕鬆的解決 50 分了。

 

  然而跳蚤國的疆域著實太大……

  考慮對於仙人掌進行點分治,然後對於當前連通塊 $T$ ,我們找點分中心 $x$ 。

  怎麼找點分中心?——類比找樹的點分中心,"找一個結點,把和它相連的邊都斷了並且他在的每一個環上的邊都要去掉(不去掉環上的其它結點)。這樣找出連通塊最大的最小作為重心。"

 

  定義連通塊的根為當前連通塊中在原圖距離 1 最近的點。

  於是我們可以分治 FFT 求出點分中心 x 到 當前連通塊的根 的多項式。

  結合點分治,總複雜度為 $O(n\log^3 n)$ ,可能可以通過此題。

 

——VFleaKing

 

  對於當前點分中心 x ,如果我們先將比 x 靠近連通塊根的那個子仙人掌處理了,那麼,我們會發現我們在這時要算的多項式大部分已經算好了。這裡只需要做的就是合併。可以發現,從 x 追根溯源得到的這些多項式有 $O(\log n)$ 個,而且他們的最高次項指數是和對應的連通塊的 size 相關的,又由於對於這些連通塊,我們做的是點分治,所以這些多項式的最高次項指數是大約 2 倍兩倍增長的。所以現在求點分中心 x 到 當前連通塊的根 的多項式,只需要 $T(n) = T(n/2) + O(n\log  n) = O(n\log n)$ 的時間複雜度。

  結合點分治,我們可以得到一個 $O(n\log ^2 n)$ 的優秀做法。

  當然,底層實現的時候還是有一些細節需要注意,這裡就不展開贅述了(饒了我吧,懶得寫了……)

  由於博主人傻常數大,所以用時差點吃雞了。

 

程式碼

#include <bits/stdc++.h>
int Debug=0;
using namespace std;
typedef long long LL;
LL read(){
	LL x=0;
	char ch=getchar();
	while (!isdigit(ch))
		ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
const int N=(1<<18)+9,mod=998244353;
int Log[N];
int Pow(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=(LL)x*x%mod)
		if (y&1)
			ans=(LL)ans*x%mod;
	return ans;
}
void Add(int &x,int y){
	if ((x+=y)>=mod)
		x-=mod;
}
int del(int x,int y){
	return x-y<0?x-y+mod:x-y;
}
int rev[N],w[N],A[N],B[N];
void FFT(int a[],int n){
	for (int i=0;i<n;i++)
		if (rev[i]<i)
			swap(a[i],a[rev[i]]);
	for (int t=n>>1,d=1;d<n;d<<=1,t>>=1)
		for (int i=0;i<n;i+=(d<<1))
			for (int j=0;j<d;j++){
				int tmp=(LL)w[j*t]*a[i+j+d]%mod;
				a[i+j+d]=del(a[i+j],tmp);
				Add(a[i+j],tmp);
			}
}
vector <int> Mul(vector <int> a,vector <int> b){
	static vector <int> ans;
	int n=1,d=0;
	for (;n<a.size()+b.size();n<<=1,d++);
	w[0]=1,w[1]=Pow(3,(mod-1)/n);
	for (int i=2;i<n;i++)
		w[i]=(LL)w[i-1]*w[1]%mod;
	for (int i=0;i<n;i++)
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(d-1));
	for (int i=0;i<n;i++)
		A[i]=B[i]=0;
	for (int i=0;i<a.size();i++)
		A[i]=a[i];
	for (int i=0;i<b.size();i++)
		B[i]=b[i];
	FFT(A,n),FFT(B,n);
	for (int i=0;i<n;i++)
		A[i]=(LL)A[i]*B[i]%mod;
	w[1]=Pow(w[1],mod-2);
	for (int i=2;i<n;i++)
		w[i]=(LL)w[i-1]*w[1]%mod;
	FFT(A,n);
	int inv=Pow(n,mod-2);
	for (int i=0;i<n;i++)
		A[i]=(LL)A[i]*inv%mod;
	while (n>0&&!A[n-1])
		n--;
	ans.clear();
	for (int i=0;i<n;i++)
		ans.push_back(A[i]);
	return ans;
}
struct poly{
	vector <int> v;
	poly(){}
	poly(int x){
		v.clear();
		while (x>=(int)v.size())
			v.push_back(0);
		v[x]++;
	}
	void add_move(poly &p,int x){
		int s=max(v.size(),p.v.size()+x);
		while (v.size()<s)
			v.push_back(0);
		for (int i=0;i<p.v.size();i++)
			Add(v[i+x],p.v[i]);
	}
	poly operator << (int x){
		poly res=*this;
		reverse(res.v.begin(),res.v.end());
		while (x--)
			res.v.push_back(0);
		reverse(res.v.begin(),res.v.end());
		return res;
	}
	void operator += (poly A){
		while (v.size()<A.v.size())
			v.push_back(0);
		for (int i=0;i<A.v.size();i++)
			Add(v[i],A.v[i]);
	}
	void operator *= (poly A){
		v=Mul(v,A.v);
	}
}C;
poly operator + (poly A,poly B){
	C.v.clear();
	for (int i=max(A.v.size(),B.v.size());i>0;i--)
		C.v.push_back(0);
	for (int i=0;i<A.v.size();i++)
		Add(C.v[i],A.v[i]);
	for (int i=0;i<B.v.size();i++)
		Add(C.v[i],B.v[i]);
	return C;
}
poly operator * (poly A,poly B){
	C.v=Mul(A.v,B.v);
	return C;
}
struct Gragh{
	static const int M=N*4;
	int cnt,y[M],nxt[M],fst[N];
	void clear(){
		cnt=1;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
int n,m,k;
int fa[N];
int dfn[N],low[N],st[N],Time=0,st_top=0,vise[N*2];
vector <int> circle[N],T[N];
void T_add(int a,int b){
	T[a].push_back(b);
	T[b].push_back(a);
}
void Tarjan(int x){
	dfn[x]=low[x]=++Time,st[++st_top]=x;
	for (int i=g.fst[x];i;i=g.nxt[i]){
		if (vise[i>>1])
			continue;
		vise[i>>1]=1;
		int y=g.y[i];
		if (!dfn[y]){
			Tarjan(y);
			low[x]=min(low[x],low[y]);
			if (low[y]>dfn[x]){
				T_add(x,y);
				fa[y]=x;
				st_top--;
			}
			else if (low[y]==dfn[x]){
				k++;
				T_add(x,k);
				circle[k].clear();
				circle[k].push_back(x);
				while (st[st_top]!=x&&low[st[st_top]]==dfn[x]){
					T_add(k,st[st_top]);
					circle[k].push_back(st[st_top]);
					fa[st[st_top]]=x;
					st_top--;
				}
			}
		}
		else
			low[x]=min(low[x],dfn[y]);
	}
}
int fad1[N],fad2[N];
int size[N],fimax[N],semax[N],vis[N],top[N];
int Tfa[N],Tdepth[N];
void dfsT(int x,int pre,int d){
	Tfa[x]=pre,Tdepth[x]=d;
	for (auto y : T[x])
		if (y!=pre)
			dfsT(y,x,d+1);
}
int Size,RT;
void get_size(int x,int pre){
	size[x]=x<=n?1:0,fimax[x]=semax[x]=0;
	for (auto y : T[x])
		if (y!=pre&&!vis[y]){
			get_size(y,x);
			size[x]+=size[y];
			if (size[y]>fimax[x])
				semax[x]=fimax[x],fimax[x]=size[y];
			else if (size[y]>semax[x])
				semax[x]=size[y];
		}
}
void get_root(int x,int pre){
	if (x<=n){
		fimax[x]=semax[x]=0;
		int f=Tfa[x];
		if (!vis[f]){
			if (f<=n)
				fimax[x]=Size-size[x];
			else
				fimax[x]=size[x]==fimax[f]?semax[f]:fimax[f];
		}
	}
	else {
		if (Size-size[x]>fimax[x])
			semax[x]=fimax[x],fimax[x]=Size-size[x];
		else if (Size-size[x]>semax[x])
			semax[x]=Size-size[x];
	}
	for (auto y : T[x])
		if (y!=pre&&!vis[y]){
			if (x<=n)
				fimax[x]=max(fimax[x],y<=n?size[y]:fimax[y]);
			get_root(y,x);
		}
	if (x<=n)
		if (!RT||fimax[RT]>fimax[x])
			RT=x;
}
poly up[N],dn[N],po,potmp;
int d_solve=0;
void solve(int x){
	get_size(x,0);
	Size=size[x],RT=0;
	get_root(x,0);
	assert(RT!=0);
	vis[x=RT]=++Time;
	top[x]=x;
	while (!vis[fa[top[x]]])
		top[x]=fa[top[x]];
	dn[x].v.push_back(0);
	Add(dn[x].v[0],1);
	for (auto y : T[x])
		if (!vis[y])
			if (y<=n){
				solve(y);
				if (Tdepth[y]>Tdepth[x])
					dn[x].add_move(dn[y],1);
			}
			else {
				vis[y]=vis[x];
				for (auto z : T[y])
					if (!vis[z]&&z!=x){
						solve(z);
						if (Tdepth[z]>Tdepth[y]){
							dn[y].add_move(dn[z],fad1[z]);
							dn[y].add_move(dn[z],fad2[z]);
						}
					}
				if (Tdepth[y]>Tdepth[x])
					dn[x].add_move(dn[y],0);
			}
	up[x].v.clear();
	if (vis[fa[x]]>vis[x]){
		int f=fa[x];
		po.v.clear();
		po=poly(0);
		while (f!=top[x]){
			if (top[f]==f){
				if (Tfa[f]>n){
					potmp.v.clear();
					potmp=po;
					po.v.clear();
					po.add_move(potmp,fad1[f]);
					po.add_move(potmp,fad2[f]);
				}
				else
					po=po<<1;
				f=fa[f];
			}
			else {
				po*=up[f];
				f=top[f];
			}
		}
		if (Tfa[x]>n){
			top[fa[x]]=top[x];
			up[fa[x]]=po;
			up[x].add_move(po,fad1[x]);
			up[x].add_move(po,fad2[x]);
		}
		else
			up[x].add_move(po,1);
	}
	else
		up[x].v.push_back(1);
	if (top[x]!=x)
		dn[top[x]]+=dn[x]*up[x];
	if (vis[Tfa[x]]>=vis[x]&&Tfa[x]>n)
		dn[top[x]]+=dn[Tfa[x]]*up[fa[x]];
}
int main(){
	Log[1]=0;
	for (int i=2;i<N;i++)
		Log[i]=Log[i>>1]+1;
	n=k=read(),m=read();
	g.clear();
	for (int i=1;i<=m;i++){
		int a=read(),b=read();
		g.add(a,b);
		g.add(b,a);
	}
	Tarjan(1);
	for (int i=n+1;i<=k;i++){
		int tmp=circle[i].size();
		for (int j=1;j<tmp;j++){
			fad1[circle[i][j]]=j;
			fad2[circle[i][j]]=tmp-j;
		}
	}
	dfsT(1,0,0);
	vis[0]=1;
	for (int i=1;i<=k;i++)
		dn[i].v.clear();
	Time=0;
	solve(1);
	while (dn[1].v.size()<n)
		dn[1].v.push_back(0);
	for (int i=1;i<n;i++)
		printf("%d\n",dn[1].v[i]);
	return 0;
}