【HDU5647 BestCoder Round 76 (div1)B】【树形DP】DZY Loves Connecting 一棵树的所有生成子树的大小乘积

本文主要是介绍【HDU5647 BestCoder Round 76 (div1)B】【树形DP】DZY Loves Connecting 一棵树的所有生成子树的大小乘积,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

DZY Loves Connecting

Accepts: 16
Submissions: 169
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 262144/262144 K (Java/Others)
问题描述
DZY有一棵nn个结点的无根树,结点按照1\sim n1n标号。DZY喜欢树上的连通集。一个连通集SS是由一些结点组成的集合,满足SS中任意两个结点u,vu,v能够用树上的路径连通,且路径上不经过SS之外的结点。显然,单独一个结点的集合也是连通集。一个连通集的大小定义为它包含的结点个数,DZY想知道所有连通集的大小之和是多少。你能帮他数一数吗?答案可能很大,请对10^9 + 7109+7取模后输出。
输入描述
第一行tt,表示有tt组数据。
接下来tt组数据。每组数据第11行一个数nn。第2\sim n2n行中,第ii行包含一个数p_ipi,表示iip_ipi有边相连。(1\le p_i \le i-1,2\le i\le n1pii1,2in)(n\ge 1n1,所有数据中的nn之和不超过200000200000)
输出描述
每组数据输出一行答案,对10^9 + 7109+7取模。
输入样例
2
1
5
1
2
2
3
输出样例
1
42
Hint
第二个样例中,树的4条边分别为(1,2),(2,3),(2,4),(3,5)。所有连通集分别是{1},{2},{3},{4},{5},{1,2},{2,3},{2,4},{3,5},{1,2,3},{1,2,4},{2,3,4},{2,3,5},{1,2,3,4},{1,2,3,5},{2,3,4,5},{1,2,3,4,5}。

If you need a larger stack size, please use #pragma comment(linker, "/STACK:102400000,102400000") and submit your solution using C++.
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 2e5+10, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
LL sum[N];	//sum[i]表示以i为根节点的所有可能的子树大小之和
LL num[N];	//num[i]表示以i为根节点的子树方案
int n, x;
vector<int>a[N];
LL ans;
void dfs(int x,int fa)
{num[x] = 1;sum[x] = 1;for (int i = a[x].size() - 1; ~i; --i){int y = a[x][i];if (y == fa)continue;dfs(y, x);sum[x] = (sum[x] * (num[y] + 1) + sum[y] * num[x]) % Z;num[x] = num[x] * (num[y] + 1) % Z;}ans = (ans + sum[x]) % Z;
}
int main()
{scanf("%d", &casenum);for (casei = 1; casei <= casenum; ++casei){scanf("%d", &n);for (int i = 1; i <= n; ++i)a[i].clear();for (int i = 2; i <= n; ++i){scanf("%d", &x);a[i].push_back(x);a[x].push_back(i);}ans = 0;dfs(1, 0);printf("%lld\n", ans);}return 0;
}
/*
【trick&&吐槽】
1,BC会爆栈BC会爆栈BC会爆栈BC会爆栈BC会爆栈BC会爆栈BC会爆栈
2,不论是用费马小定理求逆元还是拓展欧几里得求逆元,都要注意要使得gcd()==1这里不光要要求模数Z为素数。因为只是涉及到乘法还无所谓,很多时候还涉及到加法,这就可能会使得方案数为Z的倍数。于是我们要尽量避免求逆元运算。
3,留时间制造hack数据>_<,比如爆栈啦,比如子树的形态构建啦【题意】
一棵树n(2e5)个节点,问所有子树size的乘积。【类型】
树形DP【分析】
首先因为是棵树,所以不妨以1为父节点。
然后我们发现,总归要有一个节点为整棵子树的根节点的。
于是,我们枚举一个点为子树的根节点,然后求其为子树根节点时的子树的大小之和以及子树个数
定义:LL sum[N];	//sum[i]表示以i为根节点的所有可能的子树大小之和LL num[N];	//num[i]表示以i为根节点的子树方案
显然答案就是∑sum[]。然而这个DP要怎么展开呢?
void dfs(int x,int fa)
{num[x] = 1;//只算x作为根节点时的自己,方案数为1sum[x] = 1;//只算x作为根节点时的自己,子树大小和为1for (int i = a[x].size() - 1; ~i; --i){int y = a[x][i];if (y == fa)continue;dfs(y, x);//之前子树的大小会变成*(num[y]+1)种可行方案//新的子树(y)大小会变成sum[y]*num[x]种可行方案sum[x] = (sum[x] * (num[y] + 1) + sum[y] * num[x]) % Z;//子树方案会变成*(num[y]+1)种num[x] = num[x] * (num[y] + 1) % Z;}ans = (ans + sum[x]) % Z;
}
【时间复杂度&&优化】
O(n)*/



这篇关于【HDU5647 BestCoder Round 76 (div1)B】【树形DP】DZY Loves Connecting 一棵树的所有生成子树的大小乘积的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/610416

相关文章

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri