zoukankan      html  css  js  c++  java
  • 无标号有根树/无根树 计数

    无标号有根树/无根树 计数

    当然是从有根树开始啦

    树计数容易想到递归进行,设(n)个节点有根树的( ext{OGF})(F(x))

    我们考虑(F(x))作为新根节点的子树的情况,这是一个可置换的背包问题,被称为( ext{Euler})变换

    不妨对于(F(x))的每一项考虑,我们从(F_k)这么多种类的数中选择一些出来,然后组成背包

    (I=x^k)(n=F_k),那么对于这一项的变换可以表示为

    (displaystyle T(I,n)=sum_{i=0}inom{n}{i} (sum_{j=1}^{infty}I^j)^i=(sum_{j=1}^{infty}I^j+1)^n=frac{1}{(1-I)^n})

    那么得到( ext{Euler})变换

    (displaystyle mathcal{E}(F(x))=prod_{i=1}T(x^i,F_i)=prod frac{1}{(1-x^i)^{F_i}})

    两边求$ln $

    (displaystyle ln mathcal{E}(F(x))=sum_i F_iln frac{1}{(1-x^i)})

    (displaystyle ln mathcal{E}(F(x))=sum_i F_i(sum_{j=1}frac{x^{ij}}{j}))

    换循环

    (displaystyle ln mathcal{E}(F(x))=sum_i frac{1}{i}(sum_{j=1}F_j(x^i)^j)=sum frac{F(x^i)}{i})

    (displaystyle mathcal{E}(F(x))= ext{exp}(sum frac{F(x^i)}{i}))

    到这里我们得到了一个比较阳间的变换形式,那么(F(x))的递归表示就是

    (F(x)=xcdot mathcal{E}(F(x)))

    考虑用牛顿迭代求解,设已经求出(G(x)=F(x)mod x^n),我们要求(F(x)mod x^{2n})

    (displaystyle mathcal{E}(F(x))= ext{exp}(sum frac{F(x^i)}{i}))(ige 2)的项都是已知的,可以在(O(nln n))时间得到,不妨这些项为常数( ext{coef})

    则方程为(xcdot ext{exp}(F(x)+ ext{coef})-F(x)=0)

    方程函数为(f(z)=xe^{z+ ext{coef}}-z)

    (f'(z)=xe^{z+ ext{coef}}-1)

    故知(displaystyle F(x)=G(x)-frac{xe^{G(x)+ ext{coef}}-G(x)}{xe^{G(x)+ ext{coef}}-1})

    (这里没有分治解法的。。。实际我也不会)

    [ ]


    下面是无根树计数,考虑令重心为根即可

    可以直接减掉存在一个子树(> frac{n}{2})的贡献,因为不会出现置换重复,可以将这个子树从原来的树上踢掉

    剩下部分依然看成一棵有根树,也就是(F_icdot F_{n-i}(i>frac{n}{2}))

    对于(2|n)的情况,重心可能有两个,此时要减去对称情况(displaystyle inom{F_{frac{n}{2}}}{2})

    Luogu P5900 Submission

    唔-好慢

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    typedef pair <int,int> Pii;
    typedef vector <int> V;
    #define reg register
    #define mp make_pair
    #define pb push_back
    #define Mod1(x) ((x>=P)&&(x-=P))
    #define Mod2(x) ((x<0)&&(x+=P))
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    
    char IO;
    template <class T=int> T rd(){
    	T s=0; int f=0;
    	while(!isdigit(IO=getchar())) f|=IO=='-';
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return f?-s:s;
    }
    
    const int L=19,N=1<<L|10,P=998244353;
    
    ll qpow(ll x,ll k=P-2) {
    	ll res=1;
    	for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
    	return res;
    }
    
    int I[N],J[N];
    int rev[N],w[N];
    void Init(){
    	w[1<<(L-1)]=1;
    	int t=qpow(3,(P-1)>>L);
    	rep(i,(1<<(L-1))+1,1<<L) w[i]=1ll*w[i-1]*t%P;
    	drep(i,(1<<(L-1))-1,1) w[i]=w[i<<1];
    	rep(i,J[0]=1,N-1) J[i]=1ll*J[i-1]*i%P;
    	I[N-1]=qpow(J[N-1]);
    	drep(i,N-1,1) I[i-1]=1ll*I[i]*i%P;
    }
    int Init(int n){
    	int R=1,c=-1;
    	while(R<=n) R<<=1,c++;
    	rep(i,0,R-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<c);
    	return R;
    }
    void NTT(int n,V &A,int f) {
    	static ull a[N];
    	if((int)A.size()<n) A.resize(n);
    	rep(i,0,n-1) a[rev[i]]=A[i];
    	for(int i=1;i<n;i<<=1) {
    		int *e=w+i;
    		for(int l=0;l<n;l+=i*2) {
    			for(int j=l;j<l+i;++j) {
    				int t=a[j+i]*e[j-l]%P;
    				a[j+i]=a[j]+P-t;
    				a[j]+=t;
    			}
    		}
    	}
    	rep(i,0,n-1) A[i]=a[i]%P,Mod2(A[i]);
    	if(f==-1) {
    		reverse(A.begin()+1,A.end());
    		ll base=1ll*I[n]*J[n-1]%P;
    		rep(i,0,n-1) A[i]=A[i]*base%P;
    	}
    }
    
    V operator + (V a,const V &b) {
    	if(a.size()<b.size()) a.resize(b.size());
    	rep(i,0,b.size()-1) a[i]+=b[i],Mod1(a[i]);
    	return a;
    }
    V operator - (V a,const V &b) {
    	if(a.size()<b.size()) a.resize(b.size());
    	rep(i,0,b.size()-1) a[i]-=b[i],Mod2(a[i]);
    	return a;
    }
    V operator * (V a,V b) {
    	int n=a.size()-1,m=b.size()-1;
    	int R=Init(n+m);
    	NTT(R,a,1),NTT(R,b,1);
    	rep(i,0,R-1) a[i]=1ll*a[i]*b[i]%P;
    	NTT(R,a,-1),a.resize(n+m+1);
    	return a;
    }
    V operator * (V a,const int &x) {
    	for(int &i:a) i=1ll*i*x%P;
    	return a;
    }
    V operator * (const int &x,V a) { return a*x; }
    
    void println(const V &a){
    	for(int i:a) printf("%d ",i);
    	puts("");
    }
    V read(int n){
    	V A(n);
    	rep(i,0,n-1) A[i]=rd();
    	return A;
    }
    V operator ~ (V a) {
    	int n=a.size(),m=(n+1)>>1;
    	if(n==1) return assert(a[0]),V{(int)qpow(a[0])};
    	V b=a; b.resize(m),b=~b;
    	int R=Init(n*2);
    	NTT(R,a,1),NTT(R,b,1);
    	rep(i,0,R-1) a[i]=(P+2-1ll*a[i]*b[i]%P)*b[i]%P;
    	NTT(R,a,-1),a.resize(n);
    	return a;
    }
    V Deriv(V a) {
    	rep(i,0,a.size()-2) a[i]=1ll*(i+1)*a[i+1]%P;
    	a.pop_back();
    	return a;
    }
    V Integ(V a){
    	a.pb(0);
    	drep(i,a.size()-1,1) a[i]=1ll*a[i-1]*J[i-1]%P*I[i]%P;
    	a[0]=0;
    	return a;
    }
    V Ln(V a){
    	int n=a.size();
    	a=Deriv(a)*~a;
    	return a.resize(n-1),Integ(a);
    }
    V Exp(V a) {
    	if(a.size()==1) return assert(a[0]==0),V{1};
    	int n=a.size();
    	V b=a; b.resize((n+1)/2),b=Exp(b),b.resize(n);
    	a=a-Ln(b),a[0]++;
    	a=a*b,a.resize(n);
    	return a;
    }
    
    V operator << (V a,int x) {
    	a.resize(a.size()+x);
    	drep(i,a.size()-1,x) a[i]=a[i-x];
    	rep(i,0,x-1) a[i]=0;
    	return a;
    }
    V operator >> (V a,int x) {
    	if((int)a.size()<=x) return V{};
    	rep(i,x,a.size()-1) a[i-x]=a[i];
    	a.resize(a.size()-x);
    	return a;
    }
    
    V Newton(int n){
    	if(n==1) return V{0};
    	if(n==2) return V{0,1};
    	V G=Newton((n+1)/2); G.resize(n);
    	V T=G;
    	rep(i,2,n-1) {
    		int t=1ll*I[i]*J[i-1]%P;
    		rep(j,1,min(n/2,(n-1)/i)) T[i*j]=(T[i*j]+1ll*G[j]*t)%P;
    	}
    	T=Exp(T)<<1;
    	V F=G-(T-G)*~(T-V{1});
    	return F.resize(n),F;
    }
    
    int main(){
    	int n=rd()+1; Init();
    	V F=Newton(n); 
    	int ans=F[--n];
    	rep(i,1,(n-1)/2) ans=(ans-1ll*F[i]*F[n-i])%P;
    	if(~n&1) ans=(ans-1ll*F[n/2]*(F[n/2]-1)/2)%P;
    	Mod2(ans);
    	printf("%d
    ",ans);
    }
    
    
  • 相关阅读:
    学习winform第三方界面weiFenLuo.winFormsUI.Docking.dll
    C#中MySQL数据库的备份 还原 初始化
    winform学习笔记02
    mysql与sqlserver之间的关系转换
    mysql数据库使用
    python学习--导入自己的包
    thymeleaf 拼接 超链接
    @RequestParam与@PathVariable的区别
    ifram 实现左侧菜单,右侧显示内容
    Spring 整合Shiro:记住我
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14571372.html
Copyright © 2011-2022 走看看