2 1 5 1 2 2 3
1 42HintIn the second sample, the 4 edges are (1,2),(2,3),(2,4),(3,5). All the connected sets are {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++.
题意:
给你一系列的点和边,构成一棵无根树。要求计算出图中所有连通块大小之和,连通块的大小即是连通块包含多少元素。
分析:
最近恰好在练习树形dp,当时看到这题跃跃欲试,结果还是滚粗。
看官方的题解也云里雾里,只理解了把含结点n的连通块数量全部相加最后就可以得到大小之和,最后还是第二天看了题解才明白正确做法。
用f[i]表示包含i的连通块数量,g[i]表示以i为根节点的连通块的数量。
下面给出DP方程:
f[n]=f[n]*(g[son[n][i]]+1)%mod+g[n]*f[son[n][i]]%mod;
f[n]%=mod;
g[n]=g[n]*(g[son[n][i]]+1)%mod;
f[n]*(g[son[n][i]]+1):对于结点包含n的连通块数量f[n],当检测到新子结点时,自身大小要乘上以子结点为根的连通块数量,同时还要加上自身g[n]*f[son[n][i]]:子结点的连通块同样需要更新
g[n]=g[n]*(g[son[n][i]]+1):此处是对以n为根节点的连通块数量进行更新,每多一个子结点,之前的结果都要对该子树进行更新。
最后处理一下就好了,看题解的时候发现有些和自己之前码的几乎一致,细节处理不好就是做不出来。。。
#include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <cstdio> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> using namespace std; #define INF 0x3f3f3f3f const int N=200005; const int mod=1e9+7; vector<int> son[N]; long long g[N],f[N]; void dfs(int n,int fa){ for (int i=0; i<son[n].size(); i++) { if (son[n][i]==fa) { continue; } dfs(son[n][i],n); f[n]=f[n]*(g[son[n][i]]+1)%mod+g[n]*f[son[n][i]]%mod; f[n]%=mod; g[n]=g[n]*(g[son[n][i]]+1)%mod; } } int main() { int t,n; cin>>t; while (t--) { int temp; cin>>n; for (int i=0; i<=n; i++) { son[i].clear(); g[i]=f[i]=1; } for (int i=2; i<=n; i++) { scanf("%d",&temp); son[temp].push_back(i); son[i].push_back(temp); } long long sum=0; dfs(1,-1); for (int i=1; i<=n; i++) { sum+=f[i]; sum%=mod; } cout<<sum<<endl; } return 0; }