没弄明白前觉得毒瘤,弄明白之后,这TM就是数数神题啊。
然而我这道题将近一半的代码都在写多项式全家桶。。。
这道题的推导过程确实有点繁琐,但是没有办法啊,毕竟性质就是这么复杂啊。
白云和白兔(laofu出题用的NPC),老虎和蒜头(whzzt出题用的NPC),真心有毒真的牛逼啊。。。
首先简单分析一下题目的那个限制:有公共路径的点颜色必须一样。
很容易发现其实就是两棵树里面的公共边所连的点颜色要一样,这个具有传递性。
简要题意:
两棵大小为 n n n的有标号无根树 T 1 , T 2 T_1,T_2 T1,T2,每棵树上的节点标号都是 1 − n 1-n 1−n,你需要对这 2 n 2n 2n个点染色,如果边 < u , v > <u,v>同时存在于两棵树中,它们必须被染相同的颜色,求染色方案数。
你需要对于三种情况回答。
一般来说,树是由一个边的集合和一个点的集合组成的集合 T = { V , E } T=\{V,E\} T={V,E},以下的讨论为了方便将树表示为边的集合 T 1 = E 1 , T 2 = E 2 T_1=E_1,T_2=E_2 T1=E1,T2=E2。
则一个树对 < T 1 , T 2 >
直接开个set或者map或者用什么你喜欢的方式统计一下重复出现的边的个数即可。
没有勇气的同学建议干一杯伏特加再来看
考虑其中一棵树是已经确定了的。
设 Q ( S ) Q(S) Q(S)表示与 T 1 T_1 T1的交为 S S S的 T 2 T_2 T2的数量。
设 C ( S ) C(S) C(S)表示与 T 1 T_1 T1的交为 S S S的超集的 T 2 T_2 T2的数量。
根据定义 Q ( S ) = ∑ T 2 [ T 1 ∩ T 2 = S ] C ( S ) = ∑ T 2 [ S ⊆ T 1 ∩ T 2 ] Q(S)=\sum_{T_2}[T_1\cap T_2=S]\\C(S)=\sum_{T_2}[S\subseteq T_1\cap T_2] Q(S)=T2∑[T1∩T2=S]C(S)=T2∑[S⊆T1∩T2]
容易注意到当 S ⊄ T 1 S\not\subset T_1 S⊂T1有 Q ( S ) = C ( S ) = 0 Q(S)=C(S)=0 Q(S)=C(S)=0
且有 C ( S ) = ∑ S ⊆ T Q ( T ) C(S)=\sum_{S\subseteq T}Q(T) C(S)=S⊆T∑Q(T)
根据FWT可以直接得到 Q ( S ) = ∑ S ⊆ T ( − 1 ) ∣ T ∣ − ∣ S ∣ C ( T ) Q(S)=\sum_{S\subseteq T}(-1)^{|T|-|S|}C(T) Q(S)=S⊆T∑(−1)∣T∣−∣S∣C(T)
将答案写出来并考虑化简:
A n s = ∑ S ⊆ T 1 Q ( S ) y − ∣ S ∣ = ∑ S ⊆ T 1 y − ∣ S ∣ ∑ S ⊆ W ⊆ T 1 ( − 1 ) ∣ W ∣ − ∣ S ∣ C ( W ) = ∑ W ⊆ T 1 C ( W ) ( − 1 ) ∣ W ∣ ∑ S ⊆ W ( − y ) − ∣ S ∣ = ∑ W ⊆ T 1 C ( W ) ( − 1 ) ∣ W ∣ ∑ i = 0 ∣ W ∣ ( − y ) − i ( ∣ W ∣ i ) = ∑ W ⊆ T 1 C ( W ) ( − 1 ) ∣ W ∣ ( 1 − 1 y ) ∣ W ∣ = ∑ W ⊆ T 1 ( 1 y − 1 ) ∣ W ∣ C ( W ) \begin{aligned} Ans&=\sum_{S\subseteq T_1}Q(S)y^{-|S|}\\ &=\sum_{S\subseteq T_1}y^{-|S|}\sum_{S\subseteq W\subseteq T_1}(-1)^{|W|-|S|}C(W)\\ &=\sum_{W\subseteq T_1}C(W)(-1)^{|W|}\sum_{S\subseteq W}(-y)^{-|S|}\\ &=\sum_{W\subseteq T_1}C(W)(-1)^{|W|}\sum_{i=0}^{|W|}(-y)^{-i}{|W|\choose i}\\ &=\sum_{W\subseteq T_1}C(W)(-1)^{|W|}(1-\frac{1}{y})^{|W|}\\ &=\sum_{W\subseteq T_1}(\frac{1}{y}-1)^{|W|}C(W) \end{aligned} Ans=S⊆T1∑Q(S)y−∣S∣=S⊆T1∑y−∣S∣S⊆W⊆T1∑(−1)∣W∣−∣S∣C(W)=W⊆T1∑C(W)(−1)∣W∣S⊆W∑(−y)−∣S∣=W⊆T1∑C(W)(−1)∣W∣i=0∑∣W∣(−y)−i(i∣W∣)=W⊆T1∑C(W)(−1)∣W∣(1−y1)∣W∣=W⊆T1∑(y1−1)∣W∣C(W)
其实在 W ⊆ T 1 W\subseteq T_1 W⊆T1的情况下, C ( W ) C(W) C(W)可以换一个表述方式:有 W W W这个边集中所有边的树的个数。
令人惊讶的是,此时 C ( W ) C(W) C(W)有一个简洁的计算式,假设仅保留这个边集中的边得到的是 k k k个连通块,大小分别为 a 1 , a 2 , ⋯ a k a_1,a_2,\cdots a_k a1,a2,⋯ak,则在这些连通块之间加边成树的方案数是 n k − 2 ∏ i = 1 k a i n^{k-2}\prod_{i=1}^{k}a_i nk−2i=1∏kai
证明的话,请移步这篇博客:here
来我们回到原来的式子继续推。
为了方便你不用往回翻我给你复制过来了,我们刚才已经得到的式子是:
A n s = ∑ W ⊆ T 1 ( 1 y − 1 ) ∣ W ∣ C ( W ) Ans=\sum_{W\subseteq T_1}(\frac{1}{y}-1)^{|W|}C(W) Ans=W⊆T1∑(y1−1)∣W∣C(W)
以及保留 W W W中的边,得到 k = n − ∣ W ∣ k=n-|W| k=n−∣W∣个连通块,大小分别为 a 1 , a 2 , … a k a_1,a_2,\dots a_k a1,a2,…ak,连边成树的方案数为 n k − 2 ∏ i = 1 k a i n^{k-2}\prod_{i=1}^ka_i nk−2∏i=1kai。
把 C ( W ) C(W) C(W)展开得到:
A n s = ∑ W ⊆ T 1 ( 1 y − 1 ) ∣ W ∣ n n − ∣ W ∣ − 2 ∏ i = 1 n − ∣ W ∣ a i = ( 1 y − 1 ) n n 2 ∑ W ⊆ T 1 ∏ i = 1 n − ∣ W ∣ a i n 1 y − 1 \begin{aligned} Ans&=\sum_{W\subseteq T_1}(\frac{1}{y}-1)^{|W|}n^{n-|W|-2}\prod_{i=1}^{n-|W|}a_i\\ &=\frac{(\frac{1}y-1)^n}{n^2}\sum_{W\subseteq T_1}\prod_{i=1}^{n-|W|}\frac{a_in}{\frac{1}y-1} \end{aligned} Ans=W⊆T1∑(y1−1)∣W∣nn−∣W∣−2i=1∏n−∣W∣ai=n2(y1−1)nW⊆T1∑i=1∏n−∣W∣y1−1ain
前面提出来的常数丢掉不管,设 k = n 1 y − 1 k=\dfrac{n}{\frac{1}y-1} k=y1−1n,考虑我们枚举 W W W的时候实际上枚举的就是一种树上连通块的划分。
定义一个大小为 a a a的连通块权值为 k a ka ka,一个划分的权值为所有连通块的权值之积,则现在的问题就是给你 T 1 T_1 T1,请你求出所有划分的权值之和。
很容易想到树上背包,设 f [ u ] [ i ] f[u][i] f[u][i]表示以 u u u为最高点的连通块,大小为 i i i的时候的已有贡献之和。转移考虑是否断父亲边,是否成一个新的连通块即可。
以 1 1 1为根,则最后的答案就是 k ∑ i = 1 n f [ 1 ] [ i ] ∗ i k\sum\limits_{i=1}^nf[1][i]*i ki=1∑nf[1][i]∗i。
但是很遗憾,朴素的树上背包复杂度是 O ( n 2 ) O(n^2) O(n2)的过不了。
注意到答案式子里面第 i i i项有一个 ∗ i *i ∗i,这是求导后求 1 1 1的点值的形式,考虑写成生成函数的形式然后求导。
则转移式子其实就很显然了:
F u ( x ) = F u ( x ) ∗ ( k F v ′ ( 1 ) + F v ( x ) ) F_u(x)=F_u(x)*(kF_v'(1)+F_v(x)) Fu(x)=Fu(x)∗(kFv′(1)+Fv(x))
设 g u = k F u ′ ( 1 ) , f u = F u ( 1 ) g_u=kF_u'(1),f_u=F_u(1) gu=kFu′(1),fu=Fu(1),则我们希望把 g u g_u gu凑出来,上面的式子两边求导后求 1 1 1的点值再乘上 k k k即可得到:
g u = g u ( g v + f v ) + f u g v g_u=g_u(g_v+f_v)+f_ug_v gu=gu(gv+fv)+fugv
而直接对原来的转移式子求点值可以得到:
f u = f u ∗ ( g v + f v ) f_u=f_u*(g_v+f_v) fu=fu∗(gv+fv)
转移复杂度变成 O ( 1 ) O(1) O(1),直接做。
此时设 C ( W ) C(W) C(W)表示边集为 W W W的超集的树的个数,上一个子任务的容斥的直接原封不动推一遍得到:
A n s = ∑ W ( 1 y − 1 ) ∣ W ∣ C 2 ( W ) Ans=\sum_{W}(\frac{1}y-1)^|W|C^2(W) Ans=W∑(y1−1)∣W∣C2(W)
同样,将 C 2 ( W ) C^2(W) C2(W)暴力展开。
A n s = ∑ W ( 1 y − 1 ) ∣ W ∣ ( n n − ∣ W ∣ − 2 ∏ i = 1 n − ∣ W ∣ a i ) 2 = ∑ W ( 1 y − 1 ) ∣ W ∣ n 2 n − 2 ∣ W ∣ − 4 ∏ i = 1 n − ∣ W ∣ a i 2 = ( 1 y − 1 ) n n 4 ∑ W ∏ i = 1 n − ∣ W ∣ a i 2 n 2 1 y − 1 \begin{aligned} Ans&=\sum_{W}(\frac{1}y-1)^{|W|}(n^{n-|W|-2}\prod_{i=1}^{n-|W|}a_i)^2\\ &=\sum_{W}(\frac{1}y-1)^{|W|}n^{2n-2|W|-4}\prod_{i=1}^{n-|W|}a_i^2\\ &=\frac{(\frac{1}y-1)^n}{n^4}\sum_{W}\prod_{i=1}^{n-|W|}\frac{a_i^2n^2}{\frac{1}y-1} \end{aligned} Ans=W∑(y1−1)∣W∣(nn−∣W∣−2i=1∏n−∣W∣ai)2=W∑(y1−1)∣W∣n2n−2∣W∣−4i=1∏n−∣W∣ai2=n4(y1−1)nW∑i=1∏n−∣W∣y1−1ai2n2
同样,将前面的系数扔掉,考虑枚举 W W W本质上可以认为是在枚举森林。
设 k = n 2 1 y − 1 k=\dfrac{n^2}{\frac{1}y-1} k=y1−1n2,则问题变成了:一棵大小为 a a a的树的权值为 a 2 k a^2k a2k,一个森林的权值是所有树的权值之积,求所有 n n n个点的带标号森林的权值。
由于带标号,是一个非常显然的 e x p exp exp。树的情况,也就是连通情况的生成函数为
F ( x ) = ∑ i k i i x i i ! F(x)=\sum_{i}ki^i\frac{x^i}{i!} F(x)=i∑kiii!xi
求 e x p exp exp之后取第 n n n项即可。
代码:
#include
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
typedef std::pair<int,int> pii;
#define fi first
#define se secnod
cs int mod=998244353;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int po(int a,int b){
int r=1;for(;b;b>>=1,a=mul(a,a))
if(b&1)r=mul(r,a);return r;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline void ex_gcd(int a,int b,int &x,int &y){
if(!b){x=1,y=0;return ;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}
inline int inv(int a){
int x,y;ex_gcd(mod,a,y,x);
return x+(x>>31&mod);
}
inline int sqr(int a){return mul(a,a);}
int n,y,op;
namespace T0{
std::set<pii> s;
void main(){
for(int re i=1;i<n;++i){
int u=gi(),v=gi();
if(u>v)std::swap(u,v);
s.insert(pii(u,v));
}int tot=0;
for(int re i=1;i<n;++i){
int u=gi(),v=gi();
if(u>v)std::swap(u,v);
if(s.find(pii(u,v))!=s.end())++tot;
}
cout<<po(y,n-tot)<<"\n";
}
}
namespace T1{
cs int N=1e5+7;
int el[N],nxt[N<<1|1],to[N<<1|1],ec;
inline void adde(int u,int v){
nxt[++ec]=el[u],el[u]=ec,to[ec]=v;
nxt[++ec]=el[v],el[v]=ec,to[ec]=u;
}
int f[N],g[N],p,k;
void dfs(int u,int p){
f[u]=1,g[u]=k;
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p){dfs(v,u);
int tp=add(f[v],g[v]);Mul(g[u],tp);
Inc(g[u],mul(f[u],g[v]));Mul(f[u],tp);
}
}
void main(){
for(int re i=1;i<n;++i)adde(gi(),gi());
p=inv(y)-1,k=mul(inv(p),n);dfs(1,0);
cout<<mul(g[1],mul(po(mul(y,p),n),sqr(inv(n))))<<"\n";
}
}
namespace T2{
typedef std::vector<int> Poly;
cs int bit=18,SIZE=1<<bit|7;
int r[SIZE],*w[bit+1];
int fac[SIZE],inv[SIZE],ifc[SIZE];
inline void init_NTT(){
for(int re i=1;i<=bit;++i)w[i]=new int[1<<i-1];
int wn=po(3,mod-1>>bit);w[bit][0]=1;
for(int re i=1;i<(1<<bit-1);++i)w[bit][i]=mul(w[bit][i-1],wn);
for(int re i=bit-1;i;--i)
for(int re j=0;j<(1<<i-1);++j)w[i][j]=w[i+1][j<<1];
fac[0]=fac[1]=ifc[0]=ifc[1]=inv[0]=inv[1]=1;
for(int re i=2;i<SIZE;++i){
fac[i]=mul(fac[i-1],i);
inv[i]=mul(mod-mod/i,inv[mod%i]);
ifc[i]=mul(ifc[i-1],inv[i]);
}
}
inline void NTT(int *A,int len,int typ){
for(int re i=1;i<len;++i)if(i<r[i])std::swap(A[i],A[r[i]]);
for(int re i=1,d=1;i<len;++d,i<<=1)
for(int re j=0;j<len;j+=i<<1)
for(int re k=0;k<i;++k){
int &t1=A[j+k],&t2=A[i+j+k],t=mul(t2,w[d][k]);
t2=dec(t1,t),Inc(t1,t);
}
if(typ==-1){
std::reverse(A+1,A+len);
for(int re i=0,iv=inv[len];i<len;++i)Mul(A[i],iv);
}
}
inline void NTT(Poly &A,int len,int typ){NTT(&A[0],len,typ);}
inline void init_rev(int l){
for(int re i=1;i<l;++i)r[i]=r[i>>1]>>1|((i&1)?l>>1:0);
}
inline Poly operator*(Poly a,Poly b){
int n=a.size(),m=b.size(),deg=n+m-1,l=1;
while(l<deg)l<<=1;init_rev(l);
a.resize(l),NTT(a,l,1);
b.resize(l),NTT(b,l,1);
for(int re i=0;i<l;++i)Mul(a[i],b[i]);
NTT(a,l,-1);a.resize(deg);return a;
}
inline Poly Deriv(Poly a){
for(int re i=0,la=a.size();i+1<la;++i)a[i]=mul(a[i+1],i+1);
a.pop_back();return a;
}
inline Poly Integ(Poly a){a.push_back(0);
for(int re i=a.size()-1;i;--i)a[i]=mul(a[i-1],inv[i]);
a[0]=0;return a;
}
inline Poly Inv(cs Poly &a,int lim){
int n=a.size();Poly c,b(1,::inv(a[0]));
for(int re l=4;(l>>2)<lim;l<<=1){
init_rev(l);c.resize(l>>1);
for(int re i=0;i<(l>>1);++i)c[i]=i<n?a[i]:0;
c.resize(l),NTT(c,l,1);
b.resize(l),NTT(b,l,1);
for(int re i=0;i<l;++i)
Mul(b[i],dec(2,mul(b[i],c[i])));
NTT(b,l,-1),b.resize(l>>1);
}b.resize(lim);return b;
}
inline Poly Ln(Poly a,int lim){
a=Integ(Deriv(a)*Inv(a,lim));
return a.resize(lim),a;
}
inline Poly Exp(cs Poly &a){int lim=a.size();
int n=a.size();Poly c,b(1,1);
for(int re i=2;(i>>1)<lim;i<<=1){
c=Ln(b,i);Dec(c[0],1);
for(int re j=0;j<i;++j)c[j]=dec(j<n?a[j]:0,c[j]);
b=b*c;b.resize(i);
}b.resize(lim);return b;
}
void main(){
init_NTT();int p=::inv(y)-1,k=mul(::inv(p),sqr(n));
Poly f,g;f.resize(n+1);
for(int re i=1;i<=n;++i)f[i]=mul(mul(k,ifc[i]),po(i,i));
g=Exp(f);int ans=mul(g[n],fac[n]);
cout<<mul(ans,mul(po(mul(y,p),n),po(::inv(n),4)))<<"\n";
}
}
signed main(){
#ifdef zxyoi
freopen("tree.in","r",stdin);
#endif
n=gi(),y=gi(),op=gi();
if(y==1)cout<<po(n,op*(n-2)),exit(0);
switch(op){
case 0:T0::main();break;
case 1:T1::main();break;
case 2:T2::main();break;
}
return 0;
}