LOJ#2320. 「清華集訓 2017」生成樹計數

題目描述:

圖中有 nn 個連通塊,每個連通塊有 aia_i 個點,需要再連 n1n-1 條邊,使其變成一棵樹。

對一種連邊方案,設原圖中第 ii 個連通塊連出了 did_i 條邊,那麼這棵樹 TT 的價值爲val(T)=(i=1ndim)(i=1ndim)\text{val}(T)=\left(\prod_{i=1}^nd_i^m\right)*\left(\sum_{i=1}^nd_i^m\right)

求所有生成樹的價值和,模 998244353998244353

n30000,m30,1ai<998244353n\le30000,m\le30,1\le a_i< 998244353

題目分析:

LOJ討論版裏的超強題解

補充一下怎麼求等冪和 A(x)=k(i=1naik)xkA(x)=\sum_k\left(\sum_{i=1}^na_i^k\right)x^k

  • 方法一
    A(x)=i=1n11aix=i=1nji1ajxi=1n1aixA(x)=\sum_{i=1}^n\frac 1{1-a_ix}={\sum_{i=1}^n\prod_{j\neq i}1-a_jx\over \prod_{i=1}^n1-a_ix}
    分母用分治NTT求出,考慮分母的 xix^i 項的係數,它是在 nn 個單項式裏面選 ii 個取 axax 乘起來得到的
    而對於分子的 xix^i 項的係數,它是在刪去一個單項式後選 ii 個取 axax 乘起來再求和得到的。
    考慮某 ii 個取 axax 的單項式,它在分母中被算了一次,在分子中被算了 nin-i 次,所以分子的 xix^i 項係數等於分母的 xix^i 項係數乘以 nin-i

  • 方法二
    考慮用對數把A(x)A(x)的求和改爲求積:
    ln(1x)=1ixi\ln(1-x)=-\sum\frac 1ix^i,那麼就只需要求 i=1nln(1aix)\sum_{i=1}^n\ln(1-a_ix)
    i=1nln(1aix)=ln(i=1n1aix)\sum_{i=1}^n\ln(1-a_ix)=\ln\left(\prod_{i=1}^n1-a_ix\right)
    分治NTT後求 ln\ln 即可。

Code(發現用vector寫LN和EXP意外的好寫 雖然要寫個Mul函數 ):

#include<bits/stdc++.h>
#define maxn 70005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;
const int mod = 998244353;
int w[maxn],WL,r[maxn],fac[maxn],inv[maxn],invf[maxn],lg[maxn];
int Pow(int a,int b){int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod);return s;}
void init(int n){
	w[0]=WL=1; while(WL<=n) WL<<=1; w[1]=Pow(3,(mod-1)/WL);
	for(int i=2;i<=WL;i++) w[i]=1ll*w[i-1]*w[1]%mod,lg[i]=lg[i>>1]+1;
	inv[0]=inv[1]=invf[0]=invf[1]=fac[0]=fac[1]=1;
	for(int i=2;i<=WL;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod,invf[i]=1ll*invf[i-1]*inv[i]%mod;
}
int upd(int x){return x+(x>>31&mod);}
void NTT(int *a,int len,int flg){
	for(int i=0;i<len;i++) if(i<(r[i]=r[i>>1]>>1|(i&1?len>>1:0))) swap(a[i],a[r[i]]);
	for(int i=2,l=1,v;i<=len;l=i,i<<=1) for(int j=0,t=WL/i;j<len;j+=i) for(int k=j,o=0;k<j+l;k++,o+=t)
		a[k+l]=upd(a[k]-(v=1ll*w[flg^1?WL-o:o]*a[k+l]%mod)),a[k]=upd(a[k]+v-mod);
	if(flg^1) for(int i=0,Inv=mod-(mod-1)/len;i<len;i++) a[i]=1ll*a[i]*Inv%mod;
}
typedef vector<int> Poly;
Poly Mul(const Poly &A,const Poly &B){
	static int a[maxn],b[maxn]; int n=A.size(),m=B.size(),L=1<<lg[n+m-2]+1;
	if(n<=20||m<=20||n+m<=200) {rep(i,0,n+m-2) a[i]=0; rep(i,0,n-1) rep(j,0,m-1) a[i+j]=(a[i+j]+1ll*A[i]*B[j])%mod;}
	else{
		for(int i=0;i<L;i++) a[i]=i<n?A[i]:0,b[i]=i<m?B[i]:0;
		NTT(a,L,1),NTT(b,L,1); rep(i,0,L-1) a[i]=1ll*a[i]*b[i]%mod; NTT(a,L,-1);
	}
	return Poly(a,a+n+m-1);
}
Poly INV(const Poly &A,int n){
	static int B[maxn],a[maxn]; B[B[1]=0]=Pow(A[0],mod-2);
	for(int k=2,L=4;k<n<<1;k=L,L<<=1){
		memcpy(a,&A[0],min(n,k)<<2); rep(i,k,L-1) a[i]=B[i]=0;
		NTT(a,L,1),NTT(B,L,1); rep(i,0,L-1) B[i]=1ll*B[i]*upd(2-1ll*a[i]*B[i]%mod)%mod; NTT(B,L,-1);
		memset(B+k,0,k<<2);
	}
	return Poly(B,B+n);
}
Poly LN(const Poly &A,int n){
	Poly a(n-1),b(INV(A,n));
	rep(i,0,n-2) a[i]=1ll*A[i+1]*(i+1)%mod;
	a=Mul(a,b),b[0]=0;
	rep(i,1,n-1) b[i]=1ll*a[i-1]*inv[i]%mod;
	return b;
}
Poly EXP(const Poly &A,int n){
	Poly B{1},b;
	for(int k=2,L=4;k<n<<1;k=L,L<<=1){
		B.resize(k),b=LN(B,k); rep(i,0,min(n,k)-1) b[i]=upd((i==0)-b[i]+A[i]);
		B=Mul(B,b),B.resize(min(n,k));
	}
	return B;
}
int a[maxn];
Poly solve(int l,int r){
	if(l==r) return {1,mod-a[l]};
	int mid=(l+r)>>1;
	return Mul(solve(l,mid),solve(mid+1,r));
}
int n,m;
int main()
{
	scanf("%d%d",&n,&m),init(2*n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	Poly A(solve(1,n)),B(n-1);
	rep(i,0,n-2) B[i]=1ll*(n-i)*A[i]%mod; A=Mul(B,INV(A,n-1));
	Poly F(n-1); rep(i,0,n-2) F[i]=1ll*invf[i]*Pow(i+1,m)%mod;
	Poly PF(LN(F,n-1)); rep(i,0,n-2) PF[i]=1ll*PF[i]*A[i]%mod; PF=EXP(PF,n-1);
	Poly SF(n-1); rep(i,0,n-2) SF[i]=1ll*invf[i]*Pow(i+1,2*m)%mod;
	SF=Mul(SF,INV(F,n-1)); rep(i,0,n-2) SF[i]=1ll*SF[i]*A[i]%mod; SF.resize(n-1);
	int prd=1; rep(i,1,n) prd=1ll*prd*a[i]%mod;
	printf("%d\n",1ll*prd*fac[n-2]%mod*(Mul(PF,SF)[n-2])%mod);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章