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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章