Codechef TREDEG Trees and Degrees

Link
看到全是度数相关的计数就可以考虑Prüfer序列。
一个点的度数就是其在Prüfer序列中的出现次数\(+1\),而一个Prüfer序列的贡献就是其所有元素出现次数\(+1\)的乘积,我们可以把乘积拆开dp。
\(f_{x,i}\)表示考虑前\(x\)个点,Prüfer序列长度为\(i\)的答案之和。
边界就是\(f_{0,0}=1\),答案就是\(f_{n,n+2}\)
转移非常显然,枚举\(x\)的出现次数得到\(f_{x,i}=\sum\limits_{j=0}^i{i\choose j}(j+1)^Kf_{x-1,i-j}\)
显然这是个卷积的形式:\(\frac{f_{x,k}}{k!}=\sum\limits_{i+j=k}\frac{(i+1)^K}{i!}\frac{f_{x-1,j}}{j!}\)
\(P(x)=\sum\limits_{i=0}^{n-2}\frac{(i+1)^K}{i!}x^i\),那么答案就是\(\frac{[x^{n-2}]P(x)^n(n-2)!}{n^{n-2}}\),可以倍增快速幂解决。
然后考虑\(K=1\)的特殊情况,此时\(P(x)=\sum\limits_{i=0}^{n-2}\frac{i+1}{i!}x^i=(x+1)e^x\),因此\(P(x)^n=(x+1)^ne^{nx}=(\sum\limits_{i=0}^n{n\choose i}x^i)(\sum\limits_{i=0}^n\frac{n^i}{i!}x^i)\)\([x^{n-2}]P(x)^n=\sum\limits_{i=0}^{n-2}{n\choose i+2}\frac{n^i}{i!}\),直接\(O(n)\)计算即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=2000007,M=262145,P=998244353;
int read(){int x;scanf("%d",&x);return x;}
int inc(int a,int b){return a+=b-P,a+(a>>31&P);}
int dec(int a,int b){return a-=b,a+(a>>31&P);}
int mul(int a,int b){return 1ll*a*b%P;}
int pow(int a,int k=P-2){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int n,k,lim,fac[N],ifac[N],rev[M],w[M],E[M],I[M];
void init()
{
    fac[0]=1;
    for(int i=1;i<=2000000;++i) fac[i]=mul(fac[i-1],i);
    ifac[2000000]=pow(fac[2000000]);
    for(int i=2000000;i;--i) ifac[i-1]=mul(ifac[i],i);
}
void init(int n)
{
    lim=1<<(32-__builtin_clz(n)),w[lim>>1]=1;int g=pow(3,P/lim);
    for(int i=1;i<lim;++i) rev[i]=(rev[i>>1]>>1)|(i&1? lim>>1:0);
    w[lim>>1]=1;
    for(int i=(lim>>1)+1;i<lim;++i) w[i]=mul(w[i-1],g);
    for(int i=(lim>>1)-1;i;--i) w[i]=w[i<<1];
}
void NTT(int*a,int f)
{
    if(!~f) std::reverse(a+1,a+lim);
    for(int i=0;i<lim;++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
    for(int i=1;i<lim;i<<=1) for(int j=0,l=i<<1;j<lim;j+=l) for(int k=0,x;k<i;++k) x=mul(a[i+j+k],w[i+k]),a[i+j+k]=dec(a[j+k],x),a[j+k]=inc(a[j+k],x);
    if(!~f) for(int i=0,x=P-P/lim;i<lim;++i) a[i]=mul(a[i],x);
}
void solve1()
{
    int ans=0;
    for(int i=0,t=1;i<=n-2;++i) ans=inc(ans,1ll*fac[n]*ifac[i+2]%P*ifac[n-i-2]%P*t%P*ifac[i]%P),t=mul(t,n);
    printf("%d\n",mul(mul(ans,fac[n-2]),pow(pow(n,n-2))));
}
void solve2()
{
    init(2*n-4),memset(E+n-1,0,(lim-n+4)<<2);
    for(int i=0;i<=n-2;++i) E[i]=mul(pow(i+1,k),ifac[i]);
    NTT(E,1),std::fill(I,I+lim,1);
    for(int t=n;t;t>>=1)
    {
	if(t&1)
	{
	    for(int i=0;i<lim;++i) I[i]=mul(I[i],E[i]);
	    NTT(I,-1),memset(I+n-1,0,(n-2)<<2),NTT(I,1);
	}
	for(int i=0;i<lim;++i) E[i]=mul(E[i],E[i]);
	NTT(E,-1),memset(E+n-1,0,(n-2)<<2),NTT(E,1);
    }
    NTT(I,-1),printf("%d\n",mul(mul(I[n-2],fac[n-2]),pow(pow(n,n-2))));
}
int main(){init();for(int T=read();T;--T) n=read(),k=read(),k==1? solve1():solve2();}
posted @ 2020-02-03 21:30  Shiina_Mashiro  阅读(151)  评论(2编辑  收藏  举报