银联高校极客挑战赛 初赛 第二场 C. 异世界幻想 purfer序列+第二类斯特林数
题意:给出一个n表示点数,请问由n个节点构成的所有有标号无根树的叶子节点数总和。
首先这个“有标号无根树”的描述就强烈暗示与purfer序列有关了,每一个不同的purfer序列唯一地表示一种树的构成,并且这个序列对答案的贡献等于序列中没有出现过的数字的个数。
然后我们就可以去求每个恰好出现过k个颜色的序列的方案数,我们可以发现这个方案数恰好为C(n,k)*T(n,k)*k! T为第二类斯特林数,第二类斯特林数我们需要用NTT卷积的方式nlogn地求出,可以去找个板子来求。
#include<bits/stdc++.h> #define LL long long using namespace std; const int N=4e5+10; const LL P=998244353,yg=3; LL n,fac[N],inv[N],f[N],g[N],S2[N]; LL bin[N]; LL power(LL x,LL p) { LL ret=1; for (;p;p>>=1) { if (p&1) ret=(ret*x)%P; x=(x*x)%P; } return ret; } void NTT(LL *a,LL n,LL op) { //NTT:系数a数组,长度为n,op=1求值op=-1插值 for(LL i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1)); for(LL i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]); for(LL i=1;i<n;i<<=1) { LL wn=power(yg,op==1?(P-1)/(2*i):(P-1)-(P-1)/(2*i)),w,t; for(LL j=0;j<n;j+=i<<1) { w=1; for(LL k=0;k<i;k++) { t=a[i+j+k]*w%P;w=w*wn%P; a[i+j+k]=(a[j+k]-t+P)%P;a[j+k]=(a[j+k]+t)%P; } } } if(op==-1) { LL Inv=power(n,P-2); for(LL i=0;i<n;i++) a[i]=a[i]*Inv%P; } } long long C(long long n,long long m){return fac[n]*inv[m]%P*inv[n-m]%P;} int main() { cin>>n; if(n==1) { printf("1\n"); return 0; } if(n==2) { printf("2\n"); return 0; } fac[0]=inv[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%P,inv[i]=power(fac[i],P-2); for (int i=0;i<=n;i++) f[i]=(power(-1,i)+P)%P*inv[i]%P; for (int i=0;i<=n;i++) g[i]=power(i,n-2)*inv[i]%P; LL N=n-1; LL len=1;while(len<(n+1)<<1) len<<=1; NTT(f,len,1); NTT(g,len,1); for (int i=0;i<len;i++) S2[i]=(f[i]*g[i])%P; //求f.g的卷积为S2 NTT(S2,len,-1); long long ans=0; for(int i=1;i<=n-2;i++) { long long d=fac[i]*S2[i]; d%=P; //printf("%d %lld %lld\n",n-i,d,S2[i]); ans+=d*C(n,i)%P*(n-i)%P; ans%=P; } ans%=P,ans+=P,ans%=P; printf("%lld\n",ans); return 0; }