题解 P5900 【无标号无根树计数】
题意:题如其名。
原题
这里提供一份被分治
首先是
让我们把一些无标号物品分成若干组(非空),大小为
对于无标号问题,如果出现大小与方案皆相同的两组,则它们没有区别。也就是说,对于大小、方案皆相同的几组,形成它们的方案数为
那么,枚举大小
对于每个大小
即
我们可以把它化成另一个更实用的形式:
做过付公主的背包的同志应该知道:
如果没做过,可以先将其求导,一顿操作,然后积回来得到上式。
那么,
改变求和顺序,先枚举
先来看看无标号有根树的计数:
两边一起取
设函数
对这个东西上牛顿迭代:求
假设我们递归的求出了
注意到
也就是说,对于
这个东西可以调和级数得到。那么:
其中
上牛迭公式:
令
两边一起除掉
于是,可以对
这里复杂度是
最后,考虑怎么由无标号有根树得到无标号无根树:让每种情况只在根为重心时被统计一次。
也就是说,用总方案数减去根不是重心的方案数。
既然根不是重心,那么根一定会有且只有一个子树的大小大于等于
枚举这个大小
同时,如果
如果这两个重心的子树内形状一致,则会被正确统计一次。
否则,会统计两次。于是要减掉多出来的那一半,也就是
至此结束。这个做法避免了
(可能是我自带大常数……板子的实现太不优秀了)
剩下的,见代码:
#include <cstdio>
#include <cstring>
const int maxn = 6e5+5 , mod = 998244353;
template<typename T>
inline T max(const T &a,const T &b){
return a>b?a:b;
}
template<typename T>
inline void swap(T &a,T &b){
T temp=a;a=b;b=temp;
}
inline int mul(int a,int b){return 1ll*a*b%mod;}
int fastpow(int x,int y){
if(y==0) return 1;
int tmp=fastpow(x,y>>1);
return y&1?mul(mul(tmp,tmp),x):mul(tmp,tmp);
}
namespace Poly{
const int gate = 3,invg = fastpow(gate,mod-2);
int lim,lg,sing[maxn];
void init(int len){
lim = 1,lg = 0;while(lim<len) lim<<=1,lg++;
for(int i=1;i<lim;i++) sing[i]=(sing[i>>1]>>1)|((i&1)<<(lg-1));
}
void NTT(int *Steins,int type){
for(int i=0;i<lim;i++) if(i<sing[i]) swap(Steins[i],Steins[sing[i]]);
for(int len=1;len<lim;len<<=1){
int unit=fastpow(type==1?gate:invg,(mod-1)/(len<<1));
for(int i=0;i<lim;i+=(len<<1)){
int w = 1;
for(int k=0;k<len;k++,w=mul(w,unit)){
int x=Steins[i+k],y=mul(w,Steins[i+k+len]);
Steins[i+k]=(x+y)%mod,Steins[i+k+len]=(x-y)%mod;
}
}
}
if(type!=1){int Okabe=fastpow(lim,mod-2);for(int i=0;i<lim;i++) Steins[i]=mul(Steins[i],Okabe);}
}
int g[maxn];
void get_inv(int *Alpha,int len,int *Beta){
//使用前清空Beta数组
if(len==1){Beta[0]=fastpow(Alpha[0],mod-2);return ;}
get_inv(Alpha,(len+1)>>1,Beta);init(len+len-1);
for(int i=0;i<len;i++) g[i]=Alpha[i];for(int i=len;i<lim;i++) g[i]=0;
NTT(Beta,1),NTT(g,1);
for(int i=0;i<lim;i++) Beta[i]=mul(Beta[i],2-mul(Beta[i],g[i]));
NTT(Beta,-1);
for(int i=len;i<lim;i++) Beta[i]=0;
}
void convolution(int *Alpha,int la,int *Beta,int lb,int *Zeta){
init(la+lb-1);for(int i=lb;i<lim;i++) Beta[i]=0;
for(int i=la;i<lim;i++) Alpha[i]=0;
NTT(Alpha,1),NTT(Beta,1);
for(int i=0;i<lim;i++) Zeta[i]=mul(Alpha[i],Beta[i]);
NTT(Zeta,-1);
for(int i=la+lb-1;i<lim;i++) Zeta[i]=0;
}
const int inv2=fastpow(2,mod-2);
int t[maxn];
void get_sqrt(int *Alpha,int len,int *Beta){
//使用前清空Beta数组
if(len==1){Beta[0]=1;return ;}
get_sqrt(Alpha,(len+1)>>1,Beta);get_inv(Beta,len,t);
init(len+len-1);
for(int i=0;i<len;i++) g[i]=Alpha[i];for(int i=len;i<lim;i++) g[i]=0;
NTT(g,1),NTT(t,1),NTT(Beta,1);
for(int i=0;i<lim;i++) Beta[i]=mul(inv2,(Beta[i]+mul(g[i],t[i]))%mod);
NTT(Beta,-1);
for(int i=len;i<lim;i++) Beta[i]=0;for(int i=0;i<lim;i++) t[i]=0;
}
void derivative(int *Alpha,int len,int *Beta){
for(int i=1;i<len;i++) Beta[i-1]=mul(i,Alpha[i]);
Beta[len-1] = 0;
}
void integral(int *Alpha,int len,int *Beta){
for(int i=1;i<len;i++) Beta[i]=mul(Alpha[i-1],fastpow(i,mod-2));
Beta[0] = 0;
}
int f1[maxn],f2[maxn],f3[maxn];
void get_ln(int *Alpha,int len,int *Beta){
memset(f2,0,sizeof(f2));
derivative(Alpha,len,f1),get_inv(Alpha,len,f2);
convolution(f1,len,f2,len,f3);
integral(f3,len,Beta);
}
int e1[maxn],e2[maxn];
void get_exp(int *Alpha,int len,int *Beta){
//使用前清空Beta数组
if(len==1) return void(Beta[0]=1);
get_exp(Alpha,(len+1)>>1,Beta);get_ln(Beta,len,e1);
init(len+len-1);
for(int i=0;i<len;i++) e2[i]=Alpha[i];for(int i=len;i<lim;i++) e2[i]=0;
NTT(e2,1),NTT(Beta,1),NTT(e1,1);
for(int i=0;i<lim;i++) Beta[i]=mul(Beta[i],(1-e1[i]+e2[i]));
NTT(Beta,-1);
for(int i=len;i<lim;i++) Beta[i]=0;for(int i=0;i<lim;i++) e1[i]=0;
}
int g1[maxn],g2[maxn],g3[maxn];
void division(int *Alpha,int la,int *Beta,int lb,int *Zeta,int *Gamma){
if(la<lb){
for(int i=0;i<la;i++) Gamma[i] = Alpha[i];
return ;
}
for(int i=0;i<la;i++) g1[i]=Alpha[la-i-1];for(int i=0;i<lb;i++) g2[i]=Beta[lb-i-1];
for(int i=lb;i<=la-lb;i++) g2[i]=0;
Poly::init((la-lb+1)<<1|1);for(int i=0;i<lim;i++) g3[i]=0;
get_inv(g2,la-lb+1,g3);
convolution(g1,la,g3,la-lb+1,Zeta);
for(int i=0;i<=la-lb;i++) if(i<la-lb-i) swap(Zeta[i],Zeta[la-lb-i]);else break;
for(int i=la-lb+1;i<lim;i++) Zeta[i]=0;
for(int i=0;i<lb;i++) g2[i]=Beta[i];for(int i=0;i<=la-lb;i++) g3[i]=Zeta[i];
convolution(g2,lb,g3,la-lb+1,g1);
for(int i=0;i<lb;i++) Gamma[i]=(Alpha[i]-g1[i])%mod;
}
}
int n,Alpha[maxn],Beta[maxn],Gamma[maxn];
int g1[maxn],g2[maxn];
void Newton(int len,int *Alpha){
if(len==1) return void(Alpha[0]=1);
Newton((len+1)>>1,Alpha);
for(int i=0;i<len;i++) Gamma[i]=0;
for(int i=1;i<len;i++){
int Okabe = fastpow(i,mod-2);
for(int j=1;j*i<len;j++) Gamma[j*i]=(Gamma[j*i]+mul(Okabe,Alpha[j-1]))%mod;
}
//注意i从1开始,这里将g_0也顺手合并了进去
memset(g1,0,sizeof(g1));
Poly::get_ln(Alpha,len,g1);for(int i=0;i<len;i++) g1[i]=(g1[i]-Gamma[i])%mod;
memset(g2,0,sizeof(g2));Poly::get_inv(Alpha,len,g2);
g2[1]=(g2[1]-1)%mod;
memset(Beta,0,sizeof(Beta));Poly::get_inv(g2,len,Beta);
Poly::convolution(g1,len,Beta,len,g1);
for(int i=0;i<len;i++) Alpha[i]=(Alpha[i]-g1[i])%mod;
}
int main(){
scanf("%d",&n);Newton(n,Alpha);//这里得到的是除以x之后的结果
int answer = Alpha[n-1];
for(int i=(n>>1)+1;i<=n-1;i++) answer = (answer-mul(Alpha[i-1],Alpha[n-i-1]))%mod;
if(!(n&1)) answer = answer-mul(fastpow(2,mod-2),mul(Alpha[n/2-1],Alpha[n/2-1]-1));
answer%=mod;
printf("%d\n",(answer+mod)%mod);
return 0;
}
最后的最后,关于另一篇牛顿迭代题解的未解之谜2,我相信那是它写法上出了什么意外,因为我这里数组开到