测试地址:Message Passing
题目大意:
我的,每一个人知道一条独一无二的信息,每次能够选择一我的,向与他有关系的一我的传递全部他已经知道的信息,关系网是树状的,目标是让全部人都知道全部的信息,问有多少种传递信息的方案,使得传递的次数最少。
作法: 本题须要用到思惟+树形DP+组合数学。
首先,显然传递次数的下限是
,那么咱们能不能到达这个下限呢?若是能,怎么到达?其实容易观察出,咱们能够先把全部信息都汇集在某一我的,而后再让信息从这我的开始传递到全部人,这样传递次数就能达到下界,能够证实全部最优解都知足这样的过程。
因而如今就是求以某我的
为中间的汇集点时,总的方案数是多少。咱们发现,若是以
为根,从儿子向父亲连边,那么从各点传递信息到
的方案数,就等于拓扑序的数量。同理,从父亲向儿子连边时拓扑序的数量,就等于从
传递信息到各点的方案数。那么根据乘法原理,整个过程的方案数只要把这两个方案数乘起来便可。易知,一个图和其反图的拓扑序数量是相同的,因此咱们只要求从儿子向父亲连边时的方案数
便可。
咱们先随便选一个点为根,假设是
,那么咱们能够用树形DP求出
。具体来讲,用
表示以点
为根的子树的方案数,那么在合并方案时,点
必定是最后选,而它的各子树中又要知足各自的顺序,因此方案数其实是:
,其中
表示以
为根子树中的点数,把组合数拆开简化,这个式子能够写成:
预处理阶乘就能够
转移了,这样咱们就能
地算出
了。
但若是计算每一个
都要
,总的时间复杂度也是受不了的,所以咱们使用经典的方法——换根。仍是先以
为整棵树的根,令
为
的父亲,假设
已经算出,如何计算
?
要计算这个东西,首先当以
为根时,它在以
为根的树中的儿子如今依然是它的儿子,区别就是多了一棵子树,这棵子树的形态和在以
为根的树中去掉以
为根的子树相同。所以,咱们只要在
中消掉以
为根子树的贡献,而后将这个值做为正常子树对
转移便可,由于要求某个
的逆元,因此转移是
的。这也是为何上面的转移式子要写成那样的缘由:更加容易看出应该消掉哪一个部分的贡献。因而DP的复杂度就是
了,这个问题就解决了。
如下是本人代码:php
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1000000007; int T,n,first[1000010],tot,fa[1000010]={0},siz[1000010]; ll fac[1000010],inv[1000010],invfac[1000010]; ll f[1000010],ans[1000010]; struct edge { int v,next; }e[2000010]; void insert(int a,int b) { e[++tot].v=b; e[tot].next=first[a]; first[a]=tot; } ll power(ll a,ll b) { ll s=1,ss=a; while(b) { if (b&1) s=s*ss%mod; ss=ss*ss%mod;b>>=1; } return s; } void dp1(int v) { siz[v]=f[v]=1; for(int i=first[v];i;i=e[i].next) if (e[i].v!=fa[v]) { fa[e[i].v]=v; dp1(e[i].v); siz[v]+=siz[e[i].v]; f[v]=f[v]*invfac[siz[e[i].v]]%mod*f[e[i].v]%mod; } f[v]=f[v]*fac[siz[v]-1]%mod; } void dp2(int v) { if (v!=1) { ll faf=ans[fa[v]]; int fasiz=n-siz[v]; faf=faf*fac[siz[v]]%mod*power(f[v],mod-2)%mod; faf=faf*invfac[n-1]%mod*fac[fasiz-1]%mod; ans[v]=f[v]*invfac[fasiz]%mod*faf%mod; ans[v]=ans[v]*invfac[siz[v]-1]%mod*fac[n-1]%mod; } else ans[v]=f[v]; for(int i=first[v];i;i=e[i].next) if (e[i].v!=fa[v]) dp2(e[i].v); } int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); tot=0; for(int i=1;i<=n;i++) first[i]=0; fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1; for(ll i=2;i<=n;i++) { fac[i]=fac[i-1]*i%mod; inv[i]=(mod-mod/i)*inv[mod%i]%mod; invfac[i]=invfac[i-1]*inv[i]%mod; } for(int i=1;i<n;i++) { int a,b; scanf("%d%d",&a,&b); insert(a,b),insert(b,a); } dp1(1); dp2(1); ll totans=0; for(int i=1;i<=n;i++) totans=(totans+ans[i]*ans[i])%mod; printf("%lld\n",totans); } return 0; }
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1000000007; int T,n,first[1000010],tot,fa[1000010]={0},siz[1000010]; ll fac[1000010],inv[1000010],invfac[1000010]; ll f[1000010],ans[1000010]; struct edge { int v,next; }e[2000010]; void insert(int a,int b) { e[++tot].v=b; e[tot].next=first[a]; first[a]=tot; } ll power(ll a,ll b) { ll s=1,ss=a; while(b) { if (b&1) s=s*ss%mod; ss=ss*ss%mod;b>>=1; } return s; } void dp1(int v) { siz[v]=f[v]=1; for(int i=first[v];i;i=e[i].next) if (e[i].v!=fa[v]) { fa[e[i].v]=v; dp1(e[i].v); siz[v]+=siz[e[i].v]; f[v]=f[v]*invfac[siz[e[i].v]]%mod*f[e[i].v]%mod; } f[v]=f[v]*fac[siz[v]-1]%mod; } void dp2(int v) { if (v!=1) { ll faf=ans[fa[v]]; int fasiz=n-siz[v]; faf=faf*fac[siz[v]]%mod*power(f[v],mod-2)%mod; faf=faf*invfac[n-1]%mod*fac[fasiz-1]%mod; ans[v]=f[v]*invfac[fasiz]%mod*faf%mod; ans[v]=ans[v]*invfac[siz[v]-1]%mod*fac[n-1]%mod; } else ans[v]=f[v]; for(int i=first[v];i;i=e[i].next) if (e[i].v!=fa[v]) dp2(e[i].v); } int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); tot=0; for(int i=1;i<=n;i++) first[i]=0; fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1; for(ll i=2;i<=n;i++) { fac[i]=fac[i-1]*i%mod; inv[i]=(mod-mod/i)*inv[mod%i]%mod; invfac[i]=invfac[i-1]*inv[i]%mod; } for(int i=1;i<n;i++) { int a,b; scanf("%d%d",&a,&b); insert(a,b),insert(b,a); } dp1(1); dp2(1); ll totans=0; for(int i=1;i<=n;i++) totans=(totans+ans[i]*ans[i])%mod; printf("%lld\n",totans); } return 0; }