参考了:

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. grep的使用

    http://www.eguidedog.net/linux-tutorial/05-grep.php grep apple fruitlist.txt:在fruitlist.txt中查找apple字 ...

  2. 搭建ssm中遇到的问题

    1.多模块pom.xml文件无效 原因是没有指定模块

  3. Eclipse Git 插件 基本操作一【learn】

    安装GIT插件: 我的Eclipse版本为: Oxygen.2 Release (4.7.2),所以自带GIT插件,跳过安装. GIT插件配置: ①.添加好用户名和邮箱 注意下输入格式:user.na ...

  4. gdb几个操作

    如果进程转为守护进程,可设置如下跟进子进程 set follow-fork-mode child 输出变量/函数/返回值有print, call, display,自行选择 对于打印value has ...

  5. 对于未来学习Linux的决心书,以此为鉴

    学习Linux的决心书 我叫曹佳佳,来自祖国的大西北甘肃庆阳,2016年大专毕业之后从事自己的专业风力发电行业工作了两年多在从事风电行业的过程中越来越感觉到自己的薪资待遇和以后的发展空间越来越小,而且 ...

  6. java 微信开发 常用工具类(xml传输和解析 json转换对象)

    与微信通信常用工具(xml传输和解析) package com.lownsun.wechatOauth.utl; import java.io.IOException; import java.io. ...

  7. nginx配置安装

    先安装pcrepcre作用是让Nginx支持Rewrite功能下载地址:https://sourceforge.net/projects/pcre/files/pcre/,选择最新版本进行下载下载之后 ...

  8. talent-aio源码阅读小记(一)

    近来在oschina上看到一个很火的java 即时通讯项目talent-aio,恰巧想了解一下这方面的东西,就阅读了一下项目的源码,这里对自己阅读源码后的一些心得体会做一下备忘,也希望能够对其他项目中 ...

  9. RHCSA考试

      RHCSA_PDF版传送门:https://files.cnblogs.com/files/zhangjianghua/RHCSA%E8%AF%95%E9%A2%98.pdf RHCE_PDF版传 ...

  10. 03,Python网络爬虫第一弹《Python网络爬虫相关基础概念》

    爬虫介绍 引入 为什么要学习爬虫,学习爬虫能够为我们以后的发展带来那些好处?其实学习爬虫的原因和为我们以后发展带来的好处都是显而易见的,无论是从实际的应用还是从就业上. 我们都知道,当前我们所处的时代 ...