参考了:

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. linux .h .so .a文件

    在linux开发中,完全不使用第三方库的情况比较少见,通常都需要借助一个或多个函数库的支持才能完成相应功能.从程序员角度看,函数库实际上是一些头文件(.h)和库文件(.so或.a)的集合.linux下 ...

  2. CSS 滤镜技巧与细节

    本文主要介绍 CSS 滤镜的不常用用法,希望能给读者带来一些干货! 注意:ie不兼容 本文所描述的滤镜,指的是 CSS3 出来后的滤镜,不是 IE 系列时代的滤镜,话不多说,直接开车,语法如下: { ...

  3. IE 兼容模式 设置 Meta Compatible 和 Iframe 子页面的关系

    背景 因为历史原因,之前很多的系统都会是 顶级页面+Iframe来加载子级页面的这种模式构件系统,而且系统都只能运行在IE6或者IE 高版本兼容模式下(IE 7模式). 随着现在的审美原来越高,脚本能 ...

  4. ant Design表单验证笔记

    1.pattern正则验证 <Col md={12} sm={24}> <FormItem {...formItemLayout} label="班数"> ...

  5. dicom和dicomdir

    转载http://blog.sina.com.cn/s/blog_4bce5f4b01019ix5.html DICOM 文件内容在 Part 3 DICOM IOD 里定义.CT, MR, CR, ...

  6. Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting 'name pattern' at character position 36

    例: <aop:config> <aop:pointcut expression="execution(* com.zsn.Service.Impl.*.*(..))&qu ...

  7. spring-AspectJ

    动态代理 ProxyFactoryBean织入切面数量太多不利于围护 BeanNameAutoProxyCreater-------------根据Bean名称创建代理 DefaultAdvisorA ...

  8. ES6编程规范

    andre es6 js

  9. motto - question - bodyParser.urlencoded 中设置 extended 为 true 和 false 有什么区别吗?

    本文搜索关键字:motto node nodejs js javascript body-parser bodyparser urlencoded x-www-form-urlencoded exte ...

  10. MySQL超大表如何提高count速度

    经常用到count统计记录数,表又超级大,这时候sql执行很慢,就是走索引,也是很慢的,怎么办呢? 1.这个时候我们就要想为什么这么慢:根本原因是访问的数据量太大,就算只计算记录数也是很慢的. 2.如 ...