将每个独立集$V$考虑为原图中被染色的一些顶点
- 一条边的两端不同时被染色
或$V$中任意两点间没有边( ) - 没有孤立顶点被染色
$V$中的每个点都应当与$E'$中的一条边相连( )
这样问题就转化为了
这种操作可以由子图转移到大图上
-
dp[u][0]: 以u为根的子树
看作一个图( 上) $u\notin V$的情况总数, -
dp[u][1]: 以u为根的子树上
$u\in V$的情况总数, -
dp[u][2]: 以u为根的子树上
u染色且不与任一儿子连边的情况,
上面的"情况"都指除u外所有点都不孤立的情况
下面考虑转移方程
- $dp[u][0]=\Pi_{v}(2dp[v][0]+2dp[v][1]-dp[v][2])$
- $dp[u][1]=\Pi_{v}(2dp[v][0]+dp[v][1]-dp[v][2])$
- $dp[u][2]=\Pi_{v}(dp[v][0]+dp[v][1]-dp[v][2])$
第一个方程的意思是
第二个方程的意思是
第三个方程的意思是
现在考虑边界情况
对叶子节点
对根节点
dp[u][0]+dp[u][1]-dp[u][2]-1
这个式子的意思是
AC代码
#include <iostream>
#include <vector>
#define maxn 300005
#define MOD 998244353LL
using namespace std;
typedef long long ll;
ll dp[maxn][3];
vector<int> G[maxn];
bool vis[maxn];
void dfs(int i);
int main() {
int n;
cin.sync_with_stdio(0);
cin.tie(0);
cin >> n;
int a, b;
for (int i = 0; i < n - 1; i++) {
cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
dfs(1);
cout << ((dp[1][0] + dp[1][1] - dp[1][2] - 1LL + 100 * MOD) % MOD) << endl;
return 0;
}
void dfs(int u) {
vis[u] = true;
dp[u][0] = dp[u][1] = dp[u][2] = 1;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (vis[v]) continue;
dfs(v);
dp[u][0] = (dp[u][0] * (2 * dp[v][0] + 2 * dp[v][1] - dp[v][2]) + 100 * MOD) % MOD;
dp[u][1] = (dp[u][1] * (2 * dp[v][0] + dp[v][1] - dp[v][2]) + 100 * MOD) % MOD;
dp[u][2] = (dp[u][2] * (dp[v][0] + dp[v][1] - dp[v][2]) + 100 * MOD) % MOD;
}
}