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; }