HDU 4661 Message Passing 【Tree】
来源:互联网 发布:海迅开料软件 编辑:程序博客网 时间:2024/05/21 09:19
题意:
给一棵树,每一个结点都有一个信息,每一个时刻,某一对相邻的结点之间可以传递信息,那么存在一个最少的时间,使得所有的节点都可以拥有所有的信息。但是,题目不是求最短时间,而是求最短时间的情况下,有多少种传递方式:某一时刻传递信息的双方不一样则认为是不同的传递方式。(表述的不是很清楚,自己看原题了)
容易的出,最短的时间内,当然是每个节点将自己的信息想外传出去一次,并且接受一次信息,也就是树边的2倍【2*(n-1)】。
然后可以证明,在最短时间内,所有的传递方式都有一个“信息转换点”——其他节点的信息首先传递到此节点,然后信息再从这个节点向其他节点传递。
那么总方案数的计算就是可以枚举每个节点,将这个节点作为根节点,然后求当前情况下的方案——先求以当前节点为根的拓扑排序数,然后平方就是当前方案数。
拓扑排序数的计算方法:
c[i] 表示以i为根的子树的节结点(包含结点 i)
f[i] 表示以i为根的子树的拓扑排序数
则:当前结点now的拓扑排序数:
f[now] = f[son1]*f[son2]....f[sonx] * (c[now] - 1)! / (c[son1]! * c[son2]! * ... c[sonx]!)
即:所有子结点的排列数除以每颗子树所有结点的排列数(去重,对于子树的每一种拓扑序),然后乘以所有子树的拓扑序总数。
一遍dfs之后,就可以求出根节点的f值,然后在一边dfs,就可以依次求出所有节点的f值。
转移方法:
根据父节点的f值计算出去掉当前now节点之后的f值,然后以now为根节点,重新计算即可。看代码实现了。
这里用到了逆元,整个程序的速度有点慢。求逆元可以用扩展欧几里得算法,这里用的是费马定理,快速幂:x^(P-2)%P 就是x的逆元。
还有就是树dp,要避免用递归,但是还是用了,会爆栈的,所以就用手工栈了。
手工栈的方法:http://blog.csdn.net/yang_7_46/article/details/9853061
//#pragma comment(linker, "/STACK:102400000,102400000")#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <iostream>#include <vector>using namespace std;typedef long long ll;#define N 1000002#define P 1000000007llll fac[N], f[N], ff[N], cc[N], ans;vector<int> g[N];int n, c[N];ll PowerMod(ll a, ll b, ll k) { ll tmp = a, ret = 1; while (b) { if (b & 1) ret = ret * tmp % k; tmp = tmp * tmp % k; b >>= 1; } return ret;}void dfs1(int now, int fa) { int u; c[now]++; for (int i=0; i<g[now].size(); i++) if ((u = g[now][i]) != fa) { dfs1(u, now); c[now] += c[u]; } ff[now] = cc[now] = 1; for (int i=0; i<g[now].size(); i++) if ((u=g[now][i]) != fa) { ff[now] = ff[now]*f[u] % P; cc[now] = cc[now]*fac[c[u]] % P; } f[now] = (ff[now]*fac[c[now]-1]%P) * PowerMod(cc[now], P-2, P) % P;}void dfs2(int now, int fa) { int u; ll t; if (now != 1) { t = f[now]*(n-c[now])%P*cc[now]%P; t = PowerMod(t, P-2, P); f[now] = f[fa]*ff[now]%P*fac[c[now]]%P*t%P; ans = (ans + f[now]*f[now]) % P; } for (int i=0; i<g[now].size(); i++) if ((u = g[now][i]) != fa) dfs2(u, now);}int main() { int size = 256 << 20; // 256MB char *p = (char*)malloc(size) + size; __asm__("movl %0, %%esp\n" :: "r"(p)); fac[0] = 1; for (int i=1; i<N; i++) fac[i] = fac[i-1]*i % P; int T; scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i=0; i<=n; i++) { c[i] = 0; g[i].clear(); } for (int i=1, x, y; i<n; i++) { scanf("%d%d", &x, &y); g[x].push_back(y); g[y].push_back(x); } dfs1(1, 0); ans = f[1] * f[1] % P; dfs2(1, 0); printf("%I64d\n", ans); } return 0;}
- HDU 4661 Message Passing 【Tree】
- HDU 4661 Message Passing
- hdu 4661Message Passing
- HDU 4661 Message Passing
- hdu 4661 Message Passing(树形dp)
- hdu 4661 Message Passing(树形dp)
- hdu 4661 Message Passing - 树形dp
- hdu 4661 Message Passing(树形dp)
- Hdu-4661 Message Passing(树形DP)
- HDOJ 4661: Message Passing
- HDU 3410Passing the Message
- HDU 3410 Passing the Message
- hdu 3410 Passing the Message
- HDU 3410 Passing the Message
- HDU 3410 Passing the Message
- Hdu 4661 Message Passing(树形DP,扩展欧几里得)
- hdu 4661 Message Passing 树形dp (2013多校联合)
- hdu 4661 Message Passing(树形DP&组合数学)
- UTF8编码转换 CChineseCode
- MFC Windows 程序设计 第二章 在窗口中绘图
- linux命令大全
- poj1077八数码问题——境界六(IDA*)
- MFC Windows 程序设计 第三章 鼠标与键盘
- HDU 4661 Message Passing 【Tree】
- MFC Windows 程序设计 第四章 菜单
- Myeclipse项目的调试与发布
- 进程调度
- MFC Windows 程序设计 第五 MFC集合类
- linux多线程的总结(pthread用法)
- MFC Windows 程序设计 第六章 文件I/O与串行化
- MFC Windows 程序设计 第七章 控件
- Effective C++读书笔记(四) 设计与声明