[ABC303Ex] Constrained Tree Degree 题解
如果每个点的度数都知道了,那问题就转化成了 P2290 [HNOI2004]树的计数,直接求 Prufer 序列的个数即可,因为一个度数为 \(d_i\) 的点在 Prufer 序列中的出现次数是 \(d_i-1\),所以答案是:\(\frac{(n-2)!}{\prod_{i=1}^{n}(d_i-1)!}\)。
可以把 \((n-2)!\) 放到最后再乘,变一下:
\[\prod_{i=1}^{n}\frac{1}{(d_i-1)!}\times (n-2)!
\]
这题要求 \(d_i\in S\),发现前面这个东西可以用母函数来做。
令 \(F(x)=a_0+a_1x+a_2x^2+\dots+a_{n-2}x^{n-2}\),对于每个 \(i\in S\),令 \(a_{i-1}=\frac{1}{(i-1)!}\),其余的 \(a_i=0\)。
可以用多项式快速幂求出 \(F(x)^n\),然后答案就是 \(x^{n-2}\) 对应的系数乘上 \((n-2)!\)。
时间复杂度:\(O(n\log^2 n)\),可以通过本题。
注意清空多余的系数
代码:
#include<bits/stdc++.h>
#define int long long
#define mxn 1000003
#define md 998244353
#define pb push_back
#define mkp make_pair
#define ld long double
#define umap unordered_map
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
#define pq priority_queue
using namespace std;
int n,k,c,s,a[mxn],f[mxn],g[mxn],fac[mxn],ifac[mxn],rev[mxn];
int power(int x,int y){
int ans=1;
for(;y;y>>=1){
if(y&1)ans=ans*x%md;
x=x*x%md;
}
return ans;
}
void ntt(int *a,int n,int flag){
rept(i,0,n)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int h=1;h<n;h<<=1){
int x,y,s=power(3,499122176/h);
for(int j=0;j<n;j+=h<<1){
int w=1;
for(int k=j;k<j+h;++k){
x=a[k],y=w*a[k+h]%md;
a[k]=(x+y)%md;
a[k+h]=(x-y+md)%md;
w=w*s%md;
}
}
}
if(flag==-1){
int p=power(n,md-2);
reverse(a+1,a+n);
rept(i,0,n)a[i]=a[i]*p%md;
}
}
void power(int x){
g[0]=1;
for(;x;x>>=1){
if(x&1){
ntt(f,s,1);
ntt(g,s,1);
rept(i,0,s)g[i]=g[i]*f[i]%md;
ntt(g,s,-1);
ntt(f,s,-1);
rept(i,n-1,s)g[i]=0;
}
ntt(f,s,1);
rept(i,0,s)f[i]=f[i]*f[i]%md;
ntt(f,s,-1);
rept(i,n-1,s)f[i]=0;
}
}
signed main(){
scanf("%lld%lld",&n,&k);
fac[0]=1;
rep(i,1,n)fac[i]=fac[i-1]*i%md;
ifac[n]=power(fac[n],md-2);
drep(i,n,1)ifac[i-1]=ifac[i]*i%md;
rep(i,1,k)scanf("%lld",&a[i]),f[a[i]-1]+=ifac[a[i]-1];
for(c=0,s=1;s<=n<<1;s<<=1,++c);
rept(i,0,s)rev[i]=(rev[i>>1]>>1)|((i&1)<<(c-1));
power(n);
cout<<g[n-2]*fac[n-2]%md;
return 0;
}