参考了:

http://www.cnblogs.com/zhsl/archive/2013/08/10/3250755.html

http://blog.csdn.net/chaobaimingtian/article/details/9852761

题意:一个有n个节点的树,每个节点存有一份独一无二的信息,要求用最小的步数,把每个节点的信息共享给所有的节点。一个节点把自己所包含的所有信息传递给相邻的一个节点为一步。

题目不是求最小的步数,而是问最小的步数下,信息传递的方法有多少种。

分析:

  1. 最小步数:所有节点把信息传给同一个节点,再由这个节点传给其他节点。因此最小步数为树边数的2倍,即2*(n-1)。我们把这个节点称为信息交换的中心节点。
  2. 拓扑排序数:从根节点开始,沿着每个边依次遍历每个点的不同走法。比如对于树:n=4,边为(1,2)(1,3)(2,4),边的编号依次为1,2,3,设根为1,那么不同的遍历方案有(这里写的是边的序号):(1,3,2)(1,2,3)(2,1,3)三种。我们说以1为根的拓扑排序数为3。
  3. 假设所有节点把信息传给中心节点的拓扑排序数为X,那么再由这个节点传给其他节点的拓扑排序数也为X,总的方法数就是X2
  4. 枚举所有的中心节点Xi, 总方法数即为ans = sum( Xi2 ), 1 <= i<= N;
  5. 求每个节点的拓扑排序数:DFS一次,记录dp[u], cnt[u]。dp[u]为以u为根节点的子树的拓扑排序数,cnt[u]为以u为根节点的子树的节点的个数。假设v1,v2为u的两个子树,那么v1, v2合并后的拓扑排序数为:sum = dp[v1]*dp[v2]*C( cnt[v1]+cnt[v2], cnt[v1]);(C为组合数公式)对于u的所有儿子,可以采用两两合并的方法。
  6. 求以u为中心节点的拓扑排序数dp[u](即u为整棵树的根节点):再次DFS一遍。

设u的父亲为fa,因为DFS是先根序遍历,因此我们在求以u为中心节点的拓扑排序数dp[u]之前,已经先将以fa为中心节点的拓扑排序数dp[fa]求了出来,因此下面我们可以直接使用这个值。

我们将fa中除去子树u的所有子树合并成一个子树t,根据上面的式子:

#pragma comment(linker,"/STACK:102400000,102400000")
#include <cstdio>
#include <cstdlib>
#include <cstring> #define LL long long int using namespace std; const int MAXN = ;
const LL MOD = ; struct Edge
{
int v;
int next;
}; int head[MAXN];
Edge D[ MAXN << ];
int N, ans;
int EdgeN;
LL cnt[MAXN];
LL dp[MAXN];
LL fac[MAXN];
LL rev[MAXN]; void AddEdge( int u, int v )
{
D[EdgeN].v = v;
D[EdgeN].next = head[u];
head[u] = EdgeN++;
return;
} //求逆元模板
void ExGcd( LL a, LL b, LL& d, LL& x, LL& y )
{
if ( !b ) { d = a, x = , y = ; }
else
{
ExGcd( b, a % b, d, y, x );
y -= x * ( a / b );
}
return;
} LL GetInverse( LL num )
{
LL d, x, y;
ExGcd( num, MOD, d, x, y );
return ( x % MOD + MOD ) % MOD;
} //预处理出所有阶乘和逆元
void init()
{
fac[] = ;
for ( int i = ; i < MAXN; ++i )
fac[i] = ( fac[i - ] * i ) % MOD; for ( int i = ; i < MAXN; ++i )
rev[i] = GetInverse( fac[i] ); return;
} //第一次DFS,求出以cur为根的子树的节点个数cnt[u]和拓扑排序数dp[cur]
void DFS1( int cur, int fa )
{
cnt[cur] = dp[cur] = ;
for ( int i = head[cur]; i != -; i = D[i].next )
{
if ( D[i].v == fa ) continue;
DFS1( D[i].v, cur );
cnt[cur] += cnt[ D[i].v ];
dp[cur] = ( (dp[cur]*dp[ D[i].v ])%MOD * rev[cnt[D[i].v]] )%MOD;
}
dp[cur] = ( dp[cur] * fac[ cnt[cur]- ] ) % MOD;
return;
} //第二次DFS,求出以cur为中心的拓扑排序数dp[cur]
void DFS2( int cur, int fa )
{
if ( cur != )
{
dp[cur] = (( (dp[fa]*cnt[cur])%MOD )*GetInverse(N-cnt[cur]))%MOD;
ans = (ans + dp[cur]*dp[cur]%MOD)%MOD;
}
for ( int i = head[cur]; i != -; i = D[i].next )
{
if ( D[i].v == fa ) continue;
DFS2( D[i].v, cur );
}
return;
} int main()
{
init();
int T;
scanf( "%d", &T );
while ( T-- )
{
scanf( "%d", &N );
EdgeN = ;
memset( head, -, sizeof(int)*(N+) );
for ( int i = ; i < N; ++i )
{
int u, v;
scanf( "%d%d", &u, &v );
AddEdge( u, v );
AddEdge( v, u );
} DFS1( , - );
ans = dp[] * dp[] % MOD;
DFS2( , - ); printf("%I64d\n", ( ans + MOD ) % MOD );
}
return ;
}

HDU 4661 Message Passing ( 树DP + 推公式 )的更多相关文章

  1. hdu 4661 Message Passing(木DP&amp;组合数学)

    Message Passing Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Other ...

  2. HDU 4661 Message Passing 【Tree】

    题意: 给一棵树,每一个结点都有一个信息,每一个时刻,某一对相邻的结点之间可以传递信息,那么存在一个最少的时间,使得所有的节点都可以拥有所有的信息.但是,题目不是求最短时间,而是求最短时间的情况下,有 ...

  3. sgu495:概率dp / 推公式

    概率题..可以dp也可以推公式 抽象出来的题目大意: 有 n个小球,有放回的取m次  问 被取出来过的小球的个数的期望 dp维护两个状态 第 i 次取出的是 没有被取出来过的小球的 概率dp[i] 和 ...

  4. hdu4507 数位dp+推公式

    推公式的能力需要锻炼.. /* dp的时候要存结构体 里面三个元素: cnt,就是满足条件的个数 sum1,就是满足条件的数字和 sum2,满足条件的数字平方和 推导过程:还是用记忆化搜索模板 dp[ ...

  5. SGU 495 Kids and Prizes:期望dp / 概率dp / 推公式

    题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=495 题意: 有n个礼物盒,m个人. 最开始每个礼物盒中都有一个礼物. m个人依次随 ...

  6. HDU-4661 Message Passing 树形DP,排列组合

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4661 题意:有n个人呈树状结构,每个人知道一个独特的消息.每次可以让一个人将他所知的所有消息告诉和他相 ...

  7. HDU 1011 Starship Troopers (树dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 题意: 题目大意是有n个房间组成一棵树,你有m个士兵,从1号房间开始让士兵向相邻的房间出发,每个 ...

  8. HDU 4085 斯坦纳树+DP

    https://cn.vjudge.net/problem/HDU-4085 给你n,m,k ,分别表示有n个点,m条边,每条边有一个权值,表示修复这条边需要的代价 从前k个点中任取一个使其和后k个点 ...

  9. HDU 4303 Hourai Jeweled 树dp 所有权利和航点 dfs2次要

    意甲冠军: long long ans = 0; for(int i = 1; i <= n; i++) for(int j = i+1; j <= n; j++) ans += F(i, ...

随机推荐

  1. 使用MongoDB 2.6 C++驱动中的连接池

    .post p{text-indent: 2em;} MongoDB2.6的CXX驱动(mongo-cxx-driver-26compat),内置包含了数据库连接池,方便管理数据库连接,但是官方文档说 ...

  2. visual attention

    The visual attention mechanism may have at least the following basic components [Tsotsos, et. al. 19 ...

  3. 4、SpringBoot------邮件发送(2)

    开发工具:STS 代码下载链接:https://github.com/theIndoorTrain/Springboot/tree/0d6194d6ea2d7f4e19791a3d3f3167f861 ...

  4. Centos防火墙添加IP白名单

    Centos iptables防火墙添加IP白名单,指定IP可访问端口 vi /etc/sysconfig/iptables 以下为我虚拟机的防火墙为例(Centos 7) # sample conf ...

  5. 路由传参,path和query的刷新报错js文件丢失

    日常的路由跳转,基本都会用到传参,有两种方式:path + query, name + params 常用的写法: this.$router.push({ path: 'proDetail',quer ...

  6. linux数据库copy方法

    相信大多数程序员都会遇到数据库copy的问题,下面就总结几种常见的方法,针对有mysql基础的同学参考 方法一:利用sqlyog的copy database的功能,如图 这种最简单,速度比较慢: 方法 ...

  7. 理解volatile与synchronized

    用 volatile 修饰的变量可以保证线程的"可见性",也就是,任何线程修改了这个 volatile 修饰的值都会通知其他线程来主缓存中重新读取值. 下面通过例子加以说明: pu ...

  8. 动态代理和AOP

    之前说过了我对IOC的理解,这篇文章说以下我对动态代理和基本的对AOP的理解. 所谓动态代理就是,在运行时,动态创建实现了一组指定接口的实现类对象. 比如有: interface A { } inte ...

  9. php-5.6.26源代码 - PHP文件汇编成opcode、执行

    文件 php-5.6.26/Zend/zend.c ZEND_API int zend_execute_scripts(int type TSRMLS_DC, zval **retval, int f ...

  10. awk命令例子详解

    awk -F: '{print "Number of dields: "NF}' passwd 字段分隔符设为冒号,所以每条记录的字段数变成7: awk  '{print &quo ...