HDU 5647 DZY Loves Connecting 树形dp
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5647
题解:
令dp[u][0]表示u所在的子树中所有的包含i的集合数,设u的儿子为vi,则易知dp[u][0]=(dp[v1][0]+1)*...*(dp[vk][0]+1)。
令dp[u][1]表示u所在的子树中所有的包含i的集合数的大小的和,则有dp[u][1]=dp[u][1]*(dp[v][0]+1)+dp[v][1]*dp[u][0];
其中dp[u][1]*(dp[v][0]+1)表示新引入v的(dp[v][0]+1)个组合的时候,左边已知的贡献值(dp[u][1],即已知的包含节点i的集合数的大小的和)增倍之后的量。
dp[v][1]*dp[u][0]则与上面刚好反过来,考虑对于已知的dp[u][0]种集合数,儿子v的贡献值增倍后的量。
则最后的答案为dp[1][1]+...+dp[n][1]
ps: 如果还不明白dp[u][0],dp[u][1]表示的含义,可以用代码跑一下简单的样例,把它们都打印出来,应该会好理解一些。
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- using namespace std;
- const int maxn = 2e5 + ;
- const int mod = 1e9 + ;
- typedef long long LL;
- struct Edge {
- int v, ne;
- Edge(int v, int ne) :v(v), ne(ne) {}
- Edge() {}
- }egs[maxn];
- int head[maxn], tot;
- int n;
- void addEdge(int u, int v) {
- egs[tot] = Edge(v, head[u]);
- head[u] = tot++;
- }
- LL dp[maxn][];
- void solve(int u) {
- dp[u][] = dp[u][] = ;
- int p = head[u];
- while (p != -) {
- Edge& e = egs[p];
- solve(e.v);
- dp[u][] = (dp[u][] * (dp[e.v][] + ) + dp[e.v][] * dp[u][]) % mod;
- dp[u][] = dp[u][] * (dp[e.v][] + )%mod;
- p = e.ne;
- }
- }
- void init() {
- memset(head, -, sizeof(head));
- tot = ;
- }
- int main() {
- int tc;
- scanf("%d", &tc);
- while (tc--) {
- init();
- scanf("%d", &n);
- for (int i = ; i <= n; i++) {
- int v;
- scanf("%d", &v);
- addEdge(v, i);
- }
- solve();
- LL ans = ;
- for (int i = ; i <= n; i++) {
- //printf("dp[%d][1]:%d\n", i,dp[i][1]);
- ans += dp[i][];
- ans %= mod;
- }
- printf("%lld\n", ans);
- }
- return ;
- }
- /*
- 1 //testcase
- 3
- 1
- 1
- */
以下是按官方题解的思路写的代码,但是wa了,当(dp[u]+1)%mod==0的时候逆元就求不出来了。
这样写就t了,说明数据确实会出现这种情况
官方题解:
- #pragma comment(linker, "/STACK:102400000,102400000")
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- using namespace std;
- typedef long long LL;
- const int maxn = 2e5 + ;
- const int mod = 1e9 + ;
- struct Edge {
- int v, ne;
- Edge(int v,int ne):v(v),ne(ne){}
- Edge(){}
- }egs[maxn*];
- int head[maxn], tot;
- void addEdge(int u, int v) {
- egs[tot] = Edge(v, head[u]);
- head[u] = tot++;
- }
- void gcd(LL a, LL b, LL &d, LL &x, LL &y) {
- if (!b) { d = a; x = ; y = ; }
- else { gcd(b, a%b, d, y, x); y -= x*(a / b); }
- // x = (x%mod + mod) % mod;
- // y = (y%mod + mod) % mod;
- }
- LL invMod(LL a, LL b) {
- LL d, x, y;
- gcd(a, b, d, x, y);
- return (x%mod+mod)%mod;
- }
- int n;
- LL dp[maxn];
- int fa[maxn];
- //自底向上
- void dfs1(int u) {
- dp[u] = ;
- int p = head[u];
- while (p != -) {
- Edge& e = egs[p];
- dfs1(e.v);
- dp[u] *= (dp[e.v] + ); dp[u] %= mod;
- p = e.ne;
- }
- }
- //自顶向下
- void dfs2(int u) {
- if (fa[u]) {
- LL tmp = dp[fa[u]] * invMod(dp[u] + , (LL)mod) % mod;
- dp[u] = dp[u] * (tmp + ) % mod;
- }
- int p = head[u];
- while(p != -) {
- Edge& e = egs[p];
- dfs2(e.v);
- p = e.ne;
- }
- }
- void init() {
- fa[] = ;
- memset(dp, , sizeof(dp));
- memset(head, -, sizeof(head));
- tot = ;
- }
- int main() {
- // freopen("data_in.txt", "r", stdin);
- int tc;
- scanf("%d", &tc);
- while (tc--) {
- scanf("%d", &n);
- init();
- for (int i = ; i <= n; i++) {
- int x;
- scanf("%d", &x);
- addEdge(x, i);
- fa[i] = x;
- }
- dfs1();
- dfs2();
- LL ans = ;
- for (int i = ; i <= n; i++) {
- ans += dp[i]; ans %= mod;
- }
- printf("%lld\n", ans);
- }
- return ;
- }
- /*
- */
但是!这种情况比较特殊,是可以单独处理一下的,对于节点u,如果在第一次dfs中它的儿子中有(dp[v]+1)%mod==0,那么说明在第一次dfs中dp[u]=0,也就是说dp[u]+1==1,u对它的父亲是没有贡献的!,那么在第二次dfs中,dp[u]实际保存的数就是整颗树中不包含u这颗子树的所有节点中包含u的父亲的集合的个数,所以只要算(dp[u]+1)*spec(spec表示v所有的兄弟的(dp[vi]+1)的乘积,不包含v本身),就可以了,具体看代码。
- #pragma comment(linker, "/STACK:102400000,102400000")
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- using namespace std;
- typedef long long LL;
- const int maxn = 2e5 + ;
- const int mod = 1e9 + ;
- struct Edge {
- int v, ne;
- Edge(int v,int ne):v(v),ne(ne){}
- Edge(){}
- }egs[maxn*];
- int head[maxn], tot;
- void addEdge(int u, int v) {
- egs[tot] = Edge(v, head[u]);
- head[u] = tot++;
- }
- void gcd(LL a, LL b, LL &d, LL &x, LL &y) {
- if (!b) { d = a; x = ; y = ; }
- else { gcd(b, a%b, d, y, x); y -= x*(a / b); }
- }
- LL invMod(LL a, LL b) {
- LL d, x, y;
- gcd(a, b, d, x, y);
- return (x%mod+mod)%mod;
- }
- int n;
- LL dp[maxn];
- int fa[maxn];
- void dfs1(int u) {
- dp[u] = ;
- int p = head[u];
- while (p != -) {
- Edge& e = egs[p];
- dfs1(e.v);
- dp[u] *= (dp[e.v] + ); dp[u] %= mod;
- p = e.ne;
- }
- }
- void dfs2(int u,LL spec) {
- if (fa[u]) {
- LL tmp;
- if((dp[u]+)%mod)
- tmp=dp[fa[u]] * invMod(dp[u] + , mod) % mod;
- else {
- tmp = (dp[fa[u]]+) * spec % mod;
- }
- dp[u] = dp[u] * (tmp + ) % mod;
- }
- int p = head[u]; spec = ;
- int po = -,flag=;
- while(p != -) {
- Edge& e = egs[p];
- if ((dp[e.v] + ) % mod == ) {
- if(po==-) po = e.v;
- else {
- flag = ;
- dfs2(e.v, );
- }
- }
- else {
- spec *= (dp[e.v] + ); spec %= mod;
- dfs2(e.v, spec);
- }
- p = e.ne;
- }
- if (po!=-) {
- if (!flag) dfs2(po, spec);
- else dfs2(po, );
- }
- }
- void init() {
- fa[] = ;
- memset(dp, , sizeof(dp));
- memset(head, -, sizeof(head));
- tot = ;
- }
- int main() {
- //freopen("data_in.txt", "r", stdin);
- int tc;
- scanf("%d", &tc);
- while (tc--) {
- scanf("%d", &n);
- init();
- for (int i = ; i <= n; i++) {
- int x;
- scanf("%d", &x);
- addEdge(x, i);
- fa[i] = x;
- }
- dfs1();
- dfs2(,);
- LL ans = ;
- for (int i = ; i <= n; i++) {
- ans += dp[i]; ans %= mod;
- }
- printf("%lld\n", ans);
- }
- return ;
- }
- /*
- */
HDU 5647 DZY Loves Connecting 树形dp的更多相关文章
- HDU5647 DZY Loves Connecting 树形DP
(先奉上jcvb大神的官方题解)BC 76 div 1 1002 对于每个结点i,统计出f[i]表示包含i的连通集有多少个,那么容易看出答案就是所有f[i]的和. 要计算f[i]是经典的树形DP问题. ...
- 【HDU 5647】DZY Loves Connecting(树DP)
pid=5647">[HDU 5647]DZY Loves Connecting(树DP) DZY Loves Connecting Time Limit: 4000/2000 MS ...
- HDU 5646 DZY Loves Partition
题目链接: hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5646 bc:http://bestcoder.hdu.edu.cn/contests/con ...
- hdu 4514 并查集+树形dp
湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Tot ...
- [HDU 5293]Tree chain problem(树形dp+树链剖分)
[HDU 5293]Tree chain problem(树形dp+树链剖分) 题面 在一棵树中,给出若干条链和链的权值,求选取不相交的链使得权值和最大. 分析 考虑树形dp,dp[x]表示以x为子树 ...
- hdu 4003 Find Metal Mineral 树形DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 Humans have discovered a kind of new metal miner ...
- HDU 4123 Bob’s Race 树形dp+单调队列
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4123 Time Limit: 5000/2000 MS (Java/Others) Memory L ...
- HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形 ...
- HDU 5758 Explorer Bo(树形DP)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5758 [题目大意] 给出一棵树,每条路长度为1,允许从一个节点传送到任意一个节点,现在要求在传送次 ...
随机推荐
- sender是什么意思C#
/// <summary> /// sender就是事件发起者,e存储事件发起者的一些参数 /// 例如: /// private void button1_Click(object se ...
- 新建一个DataTable如何手动给其添加多条数据!
早晨起来,想起昨天利用winform做类似于sqlserver数据库导入数据功能的时候,用到了新建一个DataTable手动给其添加多条数据,平时用不到,需要的时候想不起来了,这次不妨把他记下来.以下 ...
- C++获取得到图片的坐标和rgb
#include <iostream> #include <fstream> #include <string> #include <windows.h> ...
- win php nginx 配置小细节
win下配置php Nginx 首先 下载 php-Windows版本.下载Nginx Windows 版本 1> php.ini-production 修改为 php.ini 让其成为php的 ...
- linux服务器之LVS、Nginx和HAProxy负载均衡器对比
linux服务器之LVS.Nginx和HAProxy负载均衡器对比. LVS特点: 1.抗负载能力强,使用IP负载均衡技术,只做分发,所以LVS本身并没有多少流量产生: 2.稳定性.可靠性好,自身 ...
- asp.net读取excel文件多种方法
asp.net读取excel文件的三种方法示例,包括采用OleDB读取Excel文件.引用的com组件读取Excel文件.用文件流读取. 方法一:采用OleDB读取Excel文件 把Excel文件 ...
- Ubuntu通过APT配置开发环境
apt-get install vim apt-get install ssh apt-get install apache2 apt-get install redis-server apt-get ...
- js 月历 时间函数 月份第一天 星期的判断
返回值为0-6,其中返回值0为星期天:如同,php中的日期函数一样判断.
- 傅里叶变换 fft_generic halcon
傅立叶变换(FT, Fourier Transform)的作用是将一个信号由时域变换到频域.其实就是把数据由横坐标时间.纵坐标采样值的波形图格式,转换为横坐标频率.纵坐标振幅(或相位)的频谱格式.变换 ...
- python zip函数介绍
首先用help(zip)来看一下帮助文档: