• UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT


    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ23.html

    题目传送门 - UOJ#23

    题意

      给定一个有 n 个节点的仙人掌(可能有重边)。

      对于所有的 $L(1leq Lleq n-1)$ ,求出有多少不同的从节点 1 出发的包含 L 条边的简单路径。简单路径是指不重复经过任意一点。

      $nleq 10^5$

    题解

      首先我们把走一条边看作多项式 $x^1$ ,那么一条长度为 L 的路径就是其路径上的多项式的乘积。

      接下来称“环根”为距离节点 1 最近的那个节点。

      假设 $p_v$ 为点 v 对最终答案的贡献,那么 $p_v$ 是什么?分两种情况讨论:

        1. v 属于某个环且 v 不是该环的环根。那么显然这个环的环根(设为 u)更靠近 1 ,假设 v 到 u 的两条路径长度分别是 a 和 b ,那么 $p_v=p_u imes (x^a+x^b)$ 。

        2. 假设有一个点 u 不和 v 在同一个环中,且 u 距离 1 比 v 距离 1 更近,那么 $p_v=p_u imes x$ 。

     

      于是到此为止我们已经可以轻松的解决 50 分了。

     

      然而跳蚤国的疆域着实太大……

      考虑对于仙人掌进行点分治,然后对于当前连通块 $T$ ,我们找点分中心 $x$ 。

      怎么找点分中心?——类比找树的点分中心,"找一个结点,把和它相连的边都断了并且他在的每一个环上的边都要去掉(不去掉环上的其它结点)。这样找出连通块最大的最小作为重心。"

     

      定义连通块的根为当前连通块中在原图距离 1 最近的点。

      于是我们可以分治 FFT 求出点分中心 x 到 当前连通块的根 的多项式。

      结合点分治,总复杂度为 $O(nlog^3 n)$ ,可能可以通过此题。

     

    ——VFleaKing

     

      对于当前点分中心 x ,如果我们先将比 x 靠近连通块根的那个子仙人掌处理了,那么,我们会发现我们在这时要算的多项式大部分已经算好了。这里只需要做的就是合并。可以发现,从 x 追根溯源得到的这些多项式有 $O(log n)$ 个,而且他们的最高次项指数是和对应的连通块的 size 相关的,又由于对于这些连通块,我们做的是点分治,所以这些多项式的最高次项指数是大约 2 倍两倍增长的。所以现在求点分中心 x 到 当前连通块的根 的多项式,只需要 $T(n) = T(n/2) + O(nlog  n) = O(nlog n)$ 的时间复杂度。

      结合点分治,我们可以得到一个 $O(nlog ^2 n)$ 的优秀做法。

      当然,底层实现的时候还是有一些细节需要注意,这里就不展开赘述了(饶了我吧,懒得写了……)

      由于博主人傻常数大,所以用时差点吃鸡了。

      顺便赠送样例 2 的图,以及其圆方树。

    代码

    #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
    ",dn[1].v[i]);
    	return 0;
    }
    

      

  • 相关阅读:
    【C++】虚函数
    ZF-net
    bzoj1061【NOI2008】志愿者招募
    highcharts 绘制图标的JAVASCRIPT 类库 收藏
    C语言中的const,free使用方法具体解释
    Java Transaction Management
    从有序数组中查找某个值 low_bound
    [华为机试练习题]35.找零钱
    1.9算法入门之进制转换
    uboot移植rtc
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ23.html
Copyright © 2020-2023  润新知