Codeforces Round #630 (Div. 2) F. Independent Set (树型dp)
https://codeforces.com/contest/1332/problem/F
题中描述是一棵树,给出树上子图存在独立点集的定义:G‘为全图G的子图,G‘在存在点对(u,v),E(u,v)不存在。现求G中有多少个子图可以满足存在独立点集?求所有的方案数。
题目抽象为:在树中的子树中选出一些点,它们互相之间不存在直接的连边,求这样的点集方案数。考虑树型dp做法。设计状态dp[u][0]表示当前结点u不作为独立点的方案数,dp[u][1]表示当前结点u作为独立点的方案数。
假设不考虑子图,那么dp[u][0] = ∏ dp[v][0] + dp[v][1],dp[u][1] = ∏ dp[v][0],这是一个很显然的树型dp转移方程,其中v是u的儿子结点。
由于题中说明是求子图的方案数,那么还需要考虑u和v不连边的情况了。当u和v不连边去构成独立子图时,任何的结点x不能单独作为孤立的独立点存在,也就是说x如果作为独立点,不和任何结点连边是不合法的。再设计状态f[u]为u结点作为孤立点集存在的方案数,f[u]显然是从儿子结点v转移而来,f[u] = ∏ dp[v][0] + dp[v][1] - f[v] 。为什么转移方程要减去f[v]?是因为结点v作为独立点出现时,不能单独存在,所以要令dp[v][1] - f[v]。
那么新的转移方程就是 :
dp[u][0] = ∏ dp[v][0] + dp[v][1] + dp[v][0] + dp[v][1] - f[v] (需要考虑u和v不连边的情况)
dp[u][1] = ∏ dp[v][0] + dp[v][0] + dp[v][1] - f[v] (同样需要考虑u和v不连边的情况,此时v可以是独立点)
f[u] = ∏ dp[v][0] + dp[v][1] - f[v]
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 3e5+5; 5 const ll mod = 998244353; 6 vector<int> g[maxn]; 7 ll dp[maxn][3],f[maxn]; 8 void dfs(int cur,int fa){ 9 dp[cur][0] = dp[cur][1] = f[cur] = 1; 10 for(int i = 0;i<g[cur].size();i++){ 11 int v = g[cur][i]; 12 if(v == fa) continue; 13 dfs(v,cur); 14 dp[cur][0] = dp[cur][0] * ((2 * dp[v][0] + 2 * dp[v][1] - f[v])%mod) % mod; 15 dp[cur][1] = dp[cur][1] * ((2 * dp[v][0] + dp[v][1] - f[v]) % mod) % mod; 16 f[cur] = f[cur] * ((dp[v][0] + dp[v][1] - f[v]) % mod) % mod; 17 } 18 } 19 int main() { 20 int n; 21 scanf("%d",&n); 22 for(int i = 1;i<n;i++){ 23 int u,v; 24 scanf("%d%d",&u,&v); 25 g[u].push_back(v); 26 g[v].push_back(u); 27 } 28 dfs(1,0); 29 cout<<(dp[1][0]+dp[1][1]-f[1]-1+3*mod)%mod; 30 return 0; 31 }