题目链接

题意

定义 \(F(T_1,T_2)=y^{n-common}\) 其中 \(common\) 为两棵树 \(T_1,T_2\) 的公共边条数。

三种问题

1.给定 \(T_1,T_2\)

2.给定 \(T1\),\(T_2\)任意

3.均任意

Sol

第一种 std::set<int> 存边即可

这道题的关键在第二问。

考虑要求的式子:

我们令 \(c\) 是重边条数。

\[ans=\sum_{T_2} y^{n-c}=y^n\sum_{T_2}y^{-c}
\]

一个显然的想法就是枚举 \(c\) 具体是多少然后容斥计算。

但是这里有一个更为方便的方法。

因为限制等价是恰好有 c 条重边,然后贡献是一个 \(x^k\) 的形式,我们可以套用一下二项式定理。

\[ans= y^n\sum (y^{-1}-1+1)^c\\
ans= y^n\sum \sum_{i=0}^c {c\choose i}(y^{-1}-1)^i\\
\]

容易发现即是每一个重边集合的子集都会贡献一次 \((y^{-1}-1)^i\) 的答案,其中 \(i\) 代表了重边集合的大小。

这样子答案就变成了:

\[ans=y^n\sum_{E\subseteq E_{T_1}} (y^{-1}-1)^{|E|}*F(E)
\]

其中 \(F(E)\) 表示的是一棵含有 \(E\) 这个边集的的数目。

这个大力用\(prufer\)序列推式子可以得出:

\(F(E)=n^{m-2}\prod_{i=1}^m size_i\)

令 \(m\) 是边集形成的连通块个数。

回代:

\[ans=y^n\sum_{E\subseteq E_{T_1}} (y^{-1}-1)^{|E|}n^{m-2}\prod_{i=1}^m size_i
\]

显然我们有 \(|E|=n-m\)

为了方便我们把所有和 m 无关的东西往外面放:

\[ans=(1-y)^n*n^{-2}\sum_{E\subseteq E_{T_1}} \bigg((y^{-1}-1)^{-1}n\bigg)^m\prod_{i=1}^m size_i
\]

这个东西只和连通块个数及大小有关。

看到后面带有乘法操作,那么很容易想到思考一下组合意义

可以认为就是每一个连通块里面选出一个关键点的方案数。

那么我们就是要求把树 \(T_1\) 分成若干连通块并在每一个块内选取一个关键点的方案数的和,每多选出一个连通块对答案的贡献就乘上一个 系数 \(K=n(y^{-1}-1)^{-1}\)

这个就可以直接树形 \(dp\) \(O(n)\) 解决了,设 \(f[i][0/1]\) 就行了。

接下来就是第三问了,这里显然会用到生成函数了。

答案的式子就是多了个枚举:

\[ans=(1-y)^n*n^{-2}\sum_{T_1}\sum_{E\subseteq E_{T_1}} \bigg((y^{-1}-1)^{-1}n\bigg)^m\prod_{i=1}^m size_i
\]

我们其实在式子中就没有用到过 \(T_1\),所以它也是个凑方案数的:

\[ans=(1-y)^n*n^{-2}\sum_{E} F(E)\bigg((y^{-1}-1)^{-1}n\bigg)^m\prod_{i=1}^m size_i
\]

乘上一个含有这种边集的树的方案数就行了,按照之前的结论:

\[ans=(1-y)^n*n^{-2}\sum_{E} n^{m-2}\bigg((y^{-1}-1)^{-1}n\bigg)^m\prod_{i=1}^m size_i^2
\]

\[ans=(1-y)^n*n^{-4}\sum_{E} \bigg(n^2(y^{-1}-1)^{-1}\bigg)^m\prod_{i=1}^m size_i^2
\]

美化一下式子,令 \(B=(1-y)^n*n^{-4},K=n^2(y^{-1}-1)^{-1}\)

\[ans=B\sum_{E} K^m\prod_{i=1}^m size_i^2
\]

这个生成函数构造很明显了:

设 \(g_i=K*i^2*i^{i-2}=K*i^i\) 就是 \(i\) 个点一个连通块时的后面那个式子的贡献(后面那个是这种树的方案数)

设 \(EGF\) : \(G(x)=\sum_{i=1} \frac{g_ix^i}{i!}\)

用指数型生成函数,卷积就是合并两个连通块了。

所以我们似乎要求:

\[\sum_{i=1}^n G(x)^i
\]

注意到我们会算重,那么应该是:

\[\sum_{i=1}^n \frac{G(x)^i}{i!}
\]

因为连通块之间是无序的。

所以这东西就是一个 \(e^{G(x)}\) 就做完了。

多项式 \(exp\) 即可。

code:

#include<bits/stdc++.h>
#define Set(a,b) memset(a,b,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(a))
#define Clear(a,_begin_,_end_) for(int i=_begin_;i<_end_;++i) a[i]=0
using namespace std;
const int N=1e5+10;
const int mod=998244353;
template <typename T> inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;return;
}
typedef long long ll;
template<typename T>inline void Inc(T&x,int y){x+=y;if(x>=mod) x-=mod;return;}
template<typename T>inline void Dec(T&x,int y){x-=y;if(x < 0) x+=mod;return;}
template<typename T>inline int fpow(int x,T k){int ret=1;for(;k;k>>=1,x=(ll)x*x%mod) if(k&1) ret=(ll)ret*x%mod;return ret;}
inline int Sum(int x,int y){x+=y;if(x>=mod) return x-mod;return x;}
inline int Dif(int x,int y){x-=y;if(x < 0 ) return x+mod;return x;}
int n,y,op;
namespace Task1{
set<int> E[N];
void work(){
int u,v;
for(int i=1;i<n;++i) {
init(u),init(v);if(u>v) swap(u,v);
E[u].insert(v);
}int cnt=0;
for(int i=1;i<n;++i) {
init(u),init(v);if(u>v) swap(u,v);
if(E[u].find(v)!=E[u].end()) ++cnt;
}cout<<fpow(y,n-cnt)<<endl;
}
}
namespace Task2{
int f[N][2];
struct edge{int to,next;}a[N<<1];
int head[N],cnt=0,iy;
inline void add(int x,int y){a[++cnt]=(edge){y,head[x]};head[x]=cnt;}
void dfs(int u,int fa){
f[u][0]=1,f[u][1]=1;
for(int v,i=head[u];i;i=a[i].next) {
v=a[i].to;if(v==fa) continue;dfs(v,u);
f[u][1]=Sum((ll)f[u][1]*((ll)f[v][0]*iy%mod+f[v][1])%mod,(ll)f[u][0]*f[v][1]%mod*iy%mod);
f[u][0]=(ll)f[u][0]*((ll)f[v][0]*iy%mod+f[v][1])%mod;
}
f[u][0]=(ll)f[u][0]*y%mod;
f[u][1]=(ll)f[u][1]*y%mod;
return;
}
void work(){
if(y==1) return void(printf("%d\n",fpow(n,n-2)));
for(int i=1;i<n;++i){int u,v;init(u),init(v);add(u,v),add(v,u);}
int base=(ll)fpow(1-y+mod,n)*fpow(n,mod-3)%mod;
y=(ll)fpow(fpow(y,mod-2)-1,mod-2)*n%mod;
iy=fpow(y,mod-2),dfs(1,0);int ans=f[1][1];
ans=(ll)ans*base%mod;
printf("%d\n",ans);
return;
}
}
namespace Task3{
const int MAXN=N<<2;
int rader[MAXN];
const int SIZE=sizeof(rader);
int wn[30],iwn[30],Inv[MAXN];
inline void Calcw(){for(int i=0;i<30;++i) wn[i]=fpow(3,(mod-1)/(1<<i)),iwn[i]=fpow(wn[i],mod-2);}
inline void Calc_Inversion(){Inv[1]=1;for(int i=2;i<MAXN;++i) Inv[i]=(ll)(mod-mod/i)*Inv[mod%i]%mod;}
inline int Init(int m){
int len=1,up=-1;for(;len<m;len<<=1,++up);
for(int i=0;i<len;++i) rader[i]=(rader[i>>1]>>1)|((i&1)<<up);
return len;
}
inline void NTT(int*A,int n,int f){
for(int i=0;i<n;++i) if(rader[i]>i) swap(A[rader[i]],A[i]);
for(int i=1,h=1;i<n;i<<=1,++h){
int W=(~f)? wn[h]:iwn[h];
for(int j=0,p=i<<1;j<n;j+=p)
for(int w=1,k=0;k<i;++k,w=(ll)w*W%mod) {
int X=A[j|k],Y=(ll)A[j|k|i]*w%mod;
A[j|k]=Sum(X,Y),A[j|k|i]=Dif(X,Y);
}
}if(!~f) for(int i=0;i<n;++i) A[i]=(ll)A[i]*Inv[n]%mod;
return;
}
int fac[N],finv[N];
inline void Mul(const int*a,const int*b,int*c,const int n,const int m) {
int L=n+m-1;int len=Init(L);static int A[MAXN],B[MAXN];
for(int i=0;i<n;++i) A[i]=a[i];for(int i=0;i<m;++i) B[i]=b[i];
Clear(A,n,len);Clear(B,m,len);NTT(A,len,1),NTT(B,len,1);
for(int i=0;i<len;++i) c[i]=(ll)A[i]*B[i]%mod;
NTT(c,len,-1);return;
}
inline void Poly_Inv(const int*F,int*I,const int n){
if(n==1) {memset(I,0,SIZE);I[0]=fpow(F[0],mod-2);return;}
Poly_Inv(F,I,(n+1)>>1);int L=n<<1,len=Init(L);
static int A[MAXN];for(int i=0;i<n;++i) A[i]=F[i];Clear(A,n,len);
NTT(I,len,1);NTT(A,len,1);
for(int i=0;i<len;++i) I[i]=Dif(Sum(I[i],I[i]),(ll)I[i]*I[i]%mod*A[i]%mod);
NTT(I,len,-1);Clear(I,n,len);return;
}
inline void Direv(int*A,const int n){for(int i=0;i<n;++i) A[i]=(ll)A[i+1]*(i+1)%mod;A[n-1]=0;return;}
inline void Integ(int*A,const int n){for(int i=n-1;i;--i) A[i]=(ll)A[i-1]*Inv[i]%mod; A[0]=0;return;}
inline void Poly_Ln(const int*F,int*L,const int n) {
static int A[MAXN],B[MAXN];for(int i=0;i<n;++i) A[i]=F[i];
Direv(A,n);Poly_Inv(F,B,n);Mul(A,B,L,n,n);
int TL=(n<<1)-1;Clear(L,n,TL);Integ(L,n);return;
}
inline void Poly_Exp(const int*F,int*E,const int n){
if(n==1) {memset(E,0,SIZE);E[0]=1;return;} static int A[MAXN];
Poly_Exp(F,E,(n+1)>>1);Poly_Ln(E,A,n);
for(int i=0;i<n;++i) A[i]=Dif(F[i],A[i]);Inc(A[0],1);
Mul(E,A,E,n,n);int TL=(n<<1)-1;Clear(E,n,TL);return;
}
void work(){
if(y==1) return void(printf("%d\n",fpow(n,(n-2)<<1)));
Calcw(),Calc_Inversion();
int base=(ll)fpow(1-y+mod,n)*fpow(n,mod-5)%mod;
y=(ll)fpow(fpow(y,mod-2)-1,mod-2)*n%mod*n%mod;
fac[0]=finv[0]=1;
for(int i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%mod,finv[i]=(ll)finv[i-1]*Inv[i]%mod;
static int F[MAXN];
for(int i=1;i<=n;++i) F[i]=(ll)y*fpow(i,i)%mod*finv[i]%mod;
static int E[MAXN];Poly_Exp(F,E,n+1);
int ans=(ll)E[n]*fac[n]%mod;
ans=(ll)ans*base%mod;
printf("%d\n",ans);
return;
}
}
int main()
{
init(n),init(y),init(op);
if(op==0) Task1::work();
else if(op==1) Task2::work();
else if(op==2) Task3::work();
return 0;
}

【LuoguP5206】[WC2019] 数树的更多相关文章

  1. [WC2019] 数树

    [WC2019] 数树 Zhang_RQ题解(本篇仅概述) 前言 有进步,只做了半天.... 一道具有极强综合性的数数好题! 强大的多合一题目 精确地数学推导和耐心. 有套路又不失心意. 融合了: 算 ...

  2. 洛谷 P5206: bzoj 5475: LOJ 2983: [WC2019] 数树

    一道技巧性非常强的计数题,历年WC出得最好(同时可能是比较简单)的题目之一. 题目传送门:洛谷P5206. 题意简述: 给定 \(n, y\). 一张图有 \(|V| = n\) 个点.对于两棵树 \ ...

  3. 洛谷P5206 [WC2019]数树 [容斥,DP,生成函数,NTT]

    传送门 Orz神仙题,让我长了许多见识. 长式子警告 思路 y=1 由于y=1时会导致后面一些式子未定义,先抓出来. printf("%lld",opt==0?1:(opt==1? ...

  4. [LOJ2983] [WC2019] 数树

    题目链接 LOJ:https://loj.ac/problem/2983 BZOJ:https://lydsy.com/JudgeOnline/problem.php?id=5475 洛谷:https ...

  5. 洛谷P5206 [WC2019] 数树(生成函数+容斥+矩阵树)

    题面 传送门 前置芝士 矩阵树,基本容斥原理,生成函数,多项式\(\exp\) 题解 我也想哭了--orz rqy,orz shadowice 我们设\(T1,T2\)为两棵树,并定义一个权值函数\( ...

  6. 并不对劲的bzoj5475:loj2983:p5206:[wc2019]数树

    题目大意 task0:有两棵\(n\)(n\leq10^5)个点的树\(T1,T2\),每个点的点权可以是一个在\([1,y]\)里的数,如果两个点既在\(T1\)中有直接连边,又在\(T2\)中有直 ...

  7. 洛谷 P5206 - [WC2019]数树(集合反演+NTT)

    洛谷题面传送门 神仙多项式+组合数学题,不过还是被我自己想出来了( 首先对于两棵树 \(E_1,E_2\) 而言,为它们填上 \(1\sim y\) 使其合法的方案数显然是 \(y\) 的 \(E_1 ...

  8. BZOJ5475 WC2019数树(prufer+容斥原理+树形dp+多项式exp)

    因为一大堆式子实在懒得写题解了.首先用prufer推出CF917D用到的结论,然后具体见前言不搭后语的注释. #include<iostream> #include<cstdio&g ...

  9. [题解][P5206][WC2019] 数树 (op = 1)

    简要题意 给定 \(n, y\). 一张图有 \(|V| = n\) 个点,现在给出两棵树 \(T_1=G(V, E_1)\) 和 \(T_2=G(V, E_2)\). 定义这两棵树的权值 \(F(E ...

随机推荐

  1. Elasticsearch 安装 Head 插件

    引子:在上一篇文章Elasticsearch入门(一):CentOS 7.6 安装ES 7.0.0中,我们讲了如何在CentOS 7.6环境下安装 Elasticsearch 7.0.下面,我将讲一讲 ...

  2. js里面for循环的++i与i++

    首先我们应该都知道++i与i++的区别是: ++i 是先执行 i=i+1 再使用 i 的值,而 i++ 是先使用 i 的值再执行 i=i+1: 然后我们也知道for循环的执行顺序如下: for(A;B ...

  3. 接口自动化-python unittest+requests+HTMLrunner

    从2015年毕业入行软件测试,快满4年了,之前技术分享都在百度贴吧上面,现在正式开始在博客中记录工作技术,努力成长,加油 接口测试的步骤1.组装好该接口需要的参数数据2.使用get或post附带参数数 ...

  4. Unity旋转问题的总结

    1.物体的直接旋转 transform.Rotate();这个函数是在当前状态下网某个方向旋转.并且这里可以设置为世界空间或者自身空间. transform.rotation;这里可以通过直接定义一个 ...

  5. 关于confusion_matrix()返回的矩阵的索引顺序(类别顺序)

    转载至:https://blog.csdn.net/m0_38061927/article/details/77198990 1.混淆矩阵 混淆矩阵是机器学习中总结分类模型预测结果的情形分析表,以矩阵 ...

  6. 【Qt开发】Qt在QLabel(QWidget)鼠标绘制直线和矩形框

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://devbean.blog.51cto.com/448512/243546 说实话, ...

  7. css实现毛玻璃效果

    css实现毛玻璃效果,效果图 1,html代码 <div class="mainHolder"> <div class="textHolder" ...

  8. win10 hhctrl.ocx 丢失

    1.我的是从同事电脑上复制过来的,他电脑也是win102.复制文件“hhctrl.ocx”到系统目录下  32位系统目录为:C:\WINNT\System32:64位系统为C:\Windows\Sys ...

  9. 写出java.lang.Object类的六个常用方法

    java是面向对象的语言,而Object类是java中所有类的顶级父类(根类). 每个类都使用Object类作为超类,所有对象(包括数组)都实现这个类的方法,即使一个类没有用extends明确指出继承 ...

  10. [转帖]影驰首发PCIe 4.0 SSD:群联AMD合作主控飚出5GB/s

    影驰首发PCIe 4.0 SSD:群联AMD合作主控飚出5GB/s https://www.cnbeta.com/articles/tech/851275.htm 硬件发展的真快.. AMD刚刚发布的 ...