ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P6598 烷烃计数(Burside引理/无根树转有根树/动态规划)

2021-03-05 20:02:16  阅读:191  来源: 互联网

标签:int Burside sum len 转有 lp 根树 无根树 define


P6598 烷烃计数

求解度数小于等于4的n个点的无根树个数

发现对于任意无根树有p-q+s=1,p是点等价类个数,q是边等价类个数,s是[存在两个重心]

考虑分类讨论证明:

当s=0时,任意选择一个重心作为根,那么每个等价的点上面的父亲边一定是等价的,然后根节点没有父亲,所以p=q+1

当s=1时,将两个重心看作整体,每个等价的点上面的父亲边一定是等价的,然后两个重心等价,它们对应着二者之间的边,所以p=q

那么考虑求解\(\sum p-\sum q+\sum s=ans\)然后\(\sum p\)就是本质不同有根树的个数, \(\sum q\)就是一条边连接两边两个大小之和为n的子树的方案数,\(\sum s\)就是一条边两边子树相同的方案数,这样我们就成功将无根树问题转化为有根树问题了,那就至少可以使用dynamic programing了。

然后先计算儿子个数小于等于3的有根树个数设生成函数为\(A(x)\)

设\(f_n\)表示n个点的有根树个数

根据burnside引理我们有

\[f_n=\frac{\sum_{i+j+k=n-1}f_if_jf_k+3\sum_{2i+j=n-1}f_if_j+2\sum_{3i=n-1}f_i}{6} \]

那么写成生成函数形式

\[A(x)=\frac{A^3(x)+3A(x^2)A(x)+2A(x^3)}{6}x+1 \]

那么我们现在就可以分治NTT解决这个问题了,但是有一个问题,就是这种自己卷自己的东西,比如\(A^2(x) or A^3(x)\)

我们需要分类讨论处理,因为正常分治时,需要\(A(l,mid)\)卷\(A(0,r-l)\)但是当\(l\)为0的时候\(r-l\)会大于\(mid\),这部分的值还没有求出来。

所以我们分治时考虑左边区间的最大的 \(A(x)\)的贡献,那么当\(l=0\)的时候就可以直接卷积了这部分的值都在\([l,mid]\)里,然后当\(l\ne0\)的时候正常卷积但是需要配一个系数,表示各个\(f\)作为最大值的贡献。

然后求解出\(A(x)\)后,我们可以计算\(C(x)\)表示\(\sum p和\sum q以及\sum s\)了,然后仍然利用burnside引理\(\sum p:C(x)=\frac{A^4(x)+6A^2(x)A(x^2)+3A(x^2)A(x^2)+8A(x)A(x^3)+6A(x^4)}{24}\)

\(\sum q:D(x)=\frac{(A(x)-1)^2+A(x^2)-1}{2}\)

\(\sum s:Q(x)=A(x^2)\)

最后求和就是答案了。

代码细节:

  1. 每一次NTT之前都必须重新处理len求解rev,否则会出大问题,在函数中调用rev后,直接继续使用就出错了
  2. 小心len的长度应该大于等于最后得到的多项式的长度
  3. 必须在A被转化为点值之前使用它计算系数,当它已经被转换后就失效了

代码一定要一气呵成!!!另外不要慌!!!一旦慌了就难以观察出问题的细节,一定要冷静下来,思考方法

#include<bits/stdc++.h>
#define LL long long
#define V inline void
#define I inline int
#define FOR(i,a,b) for(int i=a,end##i=b;i<=end##i;++i)
#define REP(i,a,b) for(int i=a,end##i=b;i>=end##i;--i)
#define go(i,x) for(int i=hed[x];i;i=e[i].pre)
using namespace std;
inline int read()
{
	char x='\0';
	int fh=1,sum=0;
	for(x=getchar();x<'0'||x>'9';x=getchar())if(x=='-')fh=-1;
	for(;x>='0'&&x<='9';x=getchar())sum=sum*10+x-'0';
	return fh*sum;
}
const int N=8e5+9;
const int mod=998244353;
I ksm(int a,int b)
{
	int sum=1;
	while(b)
	{
		if(b&1)sum=1LL*sum*a%mod;
		b>>=1;
		a=1LL*a*a%mod;
	}
	return sum;
}
I inv(int x){return ksm(x,mod-2);}
int jch[N],ijch[N],ni[N];
V init(int mx)
{
	jch[0]=1;
	FOR(i,1,mx)jch[i]=1LL*jch[i-1]*i%mod;
	ijch[mx]=inv(jch[mx]);
	REP(i,mx-1,0)ijch[i]=1LL*ijch[i+1]*(i+1)%mod;
	FOR(i,1,mx)ni[i]=1LL*ijch[i]*jch[i-1]%mod;
}
#define poly vector<int>
int rev[N];
V getrev(int len)
{
	FOR(i,0,len-1)
	{
		rev[i]=(rev[i>>1]>>1)|((i&1)*(len>>1));
	}
}
V NTT(poly &a,int len,int tp)
{
	a.resize(len);
	FOR(i,0,len-1)if(i<rev[i])swap(a[i],a[rev[i]]);
	for(int mid=1;mid<len;mid<<=1)
	{
		int wn=ksm(3,(mod-1)/(mid*2));
		for(int i=0;i<len;i+=mid*2)
		{
			int w=1;
			for(int j=0;j<mid;j++,w=1LL*w*wn%mod)
			{
				int x=a[i+j],y=1LL*a[i+mid+j]*w%mod;
				a[i+j]=(x+y)%mod,a[i+mid+j]=(x-y+mod)%mod;
			}
		}
	}
	if(tp==-1)
	{
		int iv=inv(len);
		FOR(i,0,len-1)a[i]=1LL*a[i]*iv%mod;
		reverse(&a[1],&a[len]);
	}
}
poly operator*(poly a,poly b)
{
	int n=a.size()+b.size()-1,len=1;
	while(len<n)len<<=1;
	getrev(len);
	NTT(a,len,1),NTT(b,len,1);
	FOR(i,0,len-1)a[i]=1LL*a[i]*b[i]%mod;
	NTT(a,len,-1);
	a.resize(n);
	return a;
}
int n;
poly A;
V solve(int lp,int rp)
{
	if(lp==rp)return (A[lp]+=(lp%3==1)*1LL*A[lp/3]*ni[3]%mod)%=mod,void();
	int mid=(lp+rp)>>1;
	solve(lp,mid);
	poly a,b,c;
	a.resize(mid-lp+1),b.resize(rp-lp+1),c.resize(rp-lp+1);
	FOR(i,0,mid-lp)a[i]=A[i+lp];
	FOR(i,0,rp-lp)b[i]=A[i],c[i]=(i%2==0)*A[i/2];
	int n=a.size()+b.size()+c.size()-1,len=1;
	while(len<n)len<<=1;
	getrev(len);
	NTT(a,len,1),NTT(b,len,1),NTT(c,len,1);
	FOR(i,0,len-1)a[i]=(1LL*a[i]*b[i]%mod*b[i]%mod*(lp?ni[2]:ni[6])%mod+1LL*a[i]*c[i]%mod*ni[2]%mod)%mod;
	NTT(a,len,-1);
	FOR(i,mid-lp,rp-lp-1)(A[lp+i+1]+=a[i])%=mod;
	solve(mid+1,rp);
}
int T;
int main()
{
	/*
	poly t(4),g(4);
	FOR(i,0,3)t[i]=g[i]=1;
	int n=t.size()+g.size()-1,len=1;
	while(len<n)len<<=1;
	getrev(len);
	NTT(t,len,1),NTT(g,len,1);
	FOR(i,0,t.size()-1)cout<<t[i]<<endl;//
	FOR(i,0,len-1)t[i]=1LL*t[i]*g[i]%mod;
	NTT(t,len,-1);
	*/
	T=read();
	init(N-9);
	n=1e5;
	
	A.resize(n+1);
	A[0]=1;
	solve(0,n);
	
	int len=1;
	while(len<4*n-1)len<<=1;
	getrev(len);
	A.resize(len);

	poly ans(len),A2(len),A3(len),C(len);
	FOR(i,0,len-1)A2[i]=(i%2==0)*A[i/2],A3[i]=(i%3==0)*A[i/3];
	FOR(i,0,n)ans[i]=(1LL*(i%4==1)*A[i/4]*ni[4]%mod+A2[i])%mod;
	
//	FOR(i,0,len-1)cout<<"A "<<i<<' '<<A[i]<<endl;//
//	FOR(i,0,len-1)cout<<"A2 "<<i<<' '<<A2[i]<<endl;
//	FOR(i,0,len-1)cout<<"A3 "<<i<<' '<<A3[i]<<endl;
	
	NTT(A,len,1),NTT(A2,len,1);
//	FOR(i,0,len-1)cout<<i<<" "<<A[i]<<endl;//
//	FOR(i,0,len-1)A[i]=1LL*A[i]*A2[i]%mod;//
//	NTT(A,len,-1);
//	FOR(i,0,len-1)cout<<A[i]<<endl;
	
	NTT(A3,len,1);
	
//	FOR(i,0,len-1)cout<<"A "<<A[i]<<endl;//
	
	FOR(i,0,len-1)C[i]=(1LL*A[i]*A[i]%mod*A[i]%mod*A[i]%mod*ni[24]%mod
	+1LL*A2[i]*A[i]%mod*A[i]%mod*ni[4]%mod
	+1LL*A2[i]*A2[i]%mod*ni[8]%mod
	+1LL*A3[i]*A[i]%mod*ni[3]%mod)%mod;
	
//	FOR(i,0,len-1)cout<<"vC "<<C[i]<<endl;
	
	NTT(C,len,-1);
	
//	FOR(i,0,len-1)cout<<"C "<<i<<' '<<C[i]<<endl;//
	
//	FOR(i,0,n)cout<<i<<' '<<ans[i]<<endl;//
	FOR(i,1,n)(ans[i]+=C[i-1])%=mod;
	
	FOR(i,0,len-1)C[i]=(1LL*A[i]*A[i]%mod*ni[2]%mod+1LL*A2[i]*ni[2]%mod-A[i]+mod)%mod;
	NTT(C,len,-1);
	FOR(i,0,n)(ans[i]+=mod-C[i])%=mod;
	
	while(T--)
	{
		n=read();
		printf("%d\n",ans[n]);
	}
	return 0;
}

标签:int,Burside,sum,len,转有,lp,根树,无根树,define
来源: https://www.cnblogs.com/dinlon/p/14483814.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有