给一颗树,每个节点有一个人,初始时每个人都有一种message,每次可以选择一个人向与他相邻的一个人传递目前他拥有的所有的信息,在保证传递次数最少的情况下,问你有多少种传递的情况。
意淫下可以看出最少次数应该是2(n-1)
,也就是先把所有的信息都传到一个人,然后他在扩散给其他人(google了也是题解里也是这样干的=-=但是有证明的那篇被po主自己删了)。
如果信息中心是i,那么i是最后一个被点到的,也就是说,对这颗树做拓扑排序下,最后选择的点是i的时候。
我们可以用树形dp解,首先我们的dp[i]
表示i的子树(包括i)做拓扑排序时最后一个节点是i的情况数,这时里目标解有距离,但是我们dp[root]
就是已root为中心时的目标解,那么这时我们反向推回去,dp[i]
就可以是我们想要的答案了。
所以两次dfs。
第一次更新
其中cnt
数组表示该颗子树上点的个树。这个式子很好理解,因为最后一个点肯定是now
,所以就是前面的点的拜访情况,有两类点,xxx个位置,怎么放,明显高中排列组合=-=。其实k叉树的话就是k类物品,xxx个位置,高中也有,但是这时候硬上公式不如两颗再两颗省心
第二次更新从root
开始往下,从上面的递推式中,我们可以找到如何将dp[now]
(这时它的意思已经变成以now
为中心的拓扑排序数)转移为dp[son]
(自己看吧=-=)。
第二次dfs之后,答案就是
平方的缘故是,dfs2结束之后,dp[i]表示的是所有信息进入i节点的方案数,则所有信息出i节点的方案数也是相同,因此是平方。
代码还没有=-=,想粗来之后还没开始写,刚刚0.99$入了以撒,我要去玩以撒=-=
UPD
呼,把130行的代码撸完了=-=,因为有一个mod放错地方wa了几下。
不过这时间不能看啊。。。就比时限5s少了那么点。。。。4900+ms。。。。
难道stl的stack这次就这么亏么。。。。
(oh w*f 在vj上勉强过了之后。。。自己去hdu就T了。。。)
UPD
换了方法,把排列的逆元先存下来,然后用公式排列的公式而不是累次求组合数求,这样的话,求逆元的函数调用会大大减少。
再交 2687ms -w- 一本满足
// whn6325689
// Mr.Phoebe
// http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;
#define CLR(x,y) memset(x,y,sizeof(x))
#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))
#define MID(x,y) (x+((y-x)>>1))
#define speed std::ios::sync_with_stdio(false);
#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
template<class T>
inline bool read(T &n)
{
T x = 0, tmp = 1;
char c = getchar();
while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
if(c == EOF) return false;
if(c == '-') c = getchar(), tmp = -1;
while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
n = x*tmp;
return true;
}
template <class T>
inline void write(T n)
{
if(n < 0)
{
putchar('-');
n = -n;
}
int len = 0,data[20];
while(n)
{
data[len++] = n%10;
n /= 10;
}
if(!len) data[len++] = 0;
while(len--) putchar(data[len]+48);
}
//-----------------------------------
const int MAXN=1000010;
const int MOD=1e9+7;
ll exGcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
ll gcd;
gcd=exGcd(b,a%b,y,x);
y-=a/b*x;
return gcd;
}
ll ff[MAXN],rev[MAXN];
inline ll Rev(int a)
{
ll x,y;
exGcd(a,MOD,x,y);
x=(x%MOD+MOD)%MOD;
return x;
}
inline void init()
{
ff[0]=1;
for(int i=1; i<=1000000; i++)
ff[i]=ff[i-1]*i%MOD, rev[i]=Rev(ff[i]);
}
struct Edge
{
int to,next;
}e[MAXN<<1];
int head[MAXN],tot,n;
ll ans;
void addedge(int u,int v)
{
e[tot].to=v;
e[tot].next=head[u];
head[u]=tot++;
}
ll dp[MAXN];
int num[MAXN];
void dfs(int u,int fa=-1)
{
dp[u]=1;num[u]=0;
for(int i=head[u],v;~i;i=e[i].next)
{
v=e[i].to;
if(v==fa) continue;
dfs(v,u);
num[u]+=num[v];
dp[u]=dp[u]*dp[v]%MOD*rev[num[v]]%MOD;
}
dp[u]=dp[u]*ff[num[u]]%MOD;
num[u]++;
}
void dfs2(int u,int fa=-1)
{
ans=(ans+dp[u]*dp[u]%MOD)%MOD;
for(int i=head[u],v;~i;i=e[i].next)
{
v=e[i].to;
if(v==fa) continue;
dp[v]=dp[u]*num[v]%MOD*Rev(n-num[v])%MOD;
/*
ll temp=dp[u]*rev(dp[v] * C(n-1,num[v])%MOD)%MOD; 除了v子树以外的所有的信息汇入u节点的方案数,组合数是为了消除v子树对全局的影响
dp[v]=dp[v]*temp%MOD*C(n-1,n-num[v])%MOD; 从父亲汇入v节点的方案数
*/
dfs2(v,u);
}
}
int main()
{
init();
int T;
read(T);
while(T--)
{
CLR(head,-1);tot=0;ans=0;
read(n);
for(int i=0,x,y;i<n-1;i++)
{
read(x),read(y);
addedge(x,y);
addedge(y,x);
}
dfs(1);
dfs2(1);
write(ans),putchar('\n');
}
return 0;
}
本题借鉴丁神题解