题目大意
给定一个 n n n 个点的基环树,现在对基环树上的点染色,使得每个点都有且仅有一个与他相连的点(不包括它自身)被染色,求最少的染色点数,或者返回无解。
思路
先考虑树的情况。
容易想到 DP,我们设 f i , 1 / 0 , 1 / 0 f_{i,1/0,1/0} fi,1/0,1/0 表示在第 i i i 个点,它有没有染色,它的儿子中有没有染过色的。
很容易写出 DP 式。
由于是基环树,我们先把环找出来,然后对于环之外的树可以以环上的点为根先做树形DP(不包括环上的点),求出来对应的 f f f 值。
然后考虑环上,照样考虑 DP,设 g i , 1 / 0 , 2 / 1 / 0 g_{i,1/0,2/1/0} gi,1/0,2/1/0 表示在第 i i i 个点,它有没有染色,它是右边还是左边还是儿子的点染色。
但是由于是环,有后效性,于是我们可以枚举一个点的状态,然后当成链来做,最后将枚举到的最后一个点的合法状态的值取最小值即可。
代码
#include <bits/stdc++.h>
using namespace std;
int n, Ecnt, last[100005], rt, to, bz[100005], fa[100005], d[100005], f[100005][2][2], ans, g[100005][2][3];
struct Edge { int to, next; } E[200005];
void addedge(int u, int v) { Ecnt++, E[Ecnt].next = last[u], last[u] = Ecnt, E[Ecnt].to = v; }
void dfs(int x) {bz[x] = 1;for (int xy = last[x]; xy; xy = E[xy].next)if (!bz[E[xy].to])fa[E[xy].to] = x, d[E[xy].to] = d[x] + 1, dfs(E[xy].to);
}
void change(int x, int y) {bz[x] = 2;if (d[x] < d[y])swap(x, y);if (x == y) {rt = x;return ;}change(fa[x], y);
}
void DP(int x, int fa) {int son = 0;for (int xy = last[x]; xy; xy = E[xy].next)if (!bz[E[xy].to])son++, bz[E[xy].to] = 1, DP(E[xy].to, x), f[x][0][0] = f[x][0][0] + f[E[xy].to][0][1], f[x][1][0] = f[x][1][0] + f[E[xy].to][0][0];for (int xy = last[x]; xy; xy = E[xy].next)if (bz[E[xy].to] == 1 && E[xy].to != fa) {f[x][0][1] = min(f[x][0][1], f[x][0][0] - f[E[xy].to][0][1] + f[E[xy].to][1][1]);f[x][1][1] = min(f[x][1][1], f[x][1][0] - f[E[xy].to][0][0] + f[E[xy].to][1][0]);}f[x][1][0]++, f[x][1][1]++;
}
void get(int x, int fa) {for (int xy = last[x]; xy; xy = E[xy].next)if (bz[E[xy].to] == 2 && E[xy].to != fa && E[xy].to != to) {if (E[xy].to == rt) {to = x;return ;}for (int have = 0; have < 2; have++)for (int where = 0; where < 3; where++) {if (have == 1)g[E[xy].to][have][where] = min(g[E[xy].to][have][where], g[x][where == 1][2] + f[E[xy].to][have][where == 0]);elseg[E[xy].to][have][where] = min(g[E[xy].to][have][where], min(g[x][where == 1][0], g[x][where == 1][1]) + f[E[xy].to][have][where == 0]);}get(E[xy].to, x);}
}
void solve() {for (int i = 1; i <= n; i++)f[i][1][1] = f[i][0][1] = n + 1;ans = n + 1;for (int i = 1; i <= n; i++)if (bz[i] == 2)DP(i, 0);//以环上的点为根做树形DPfor (int have = 0; have < 2; have++) {for (int where = 0; where < 3; where++) {for (int i = 1; i <= n; i++)for (int j = 0; j < 3; j++)g[i][0][j] = g[i][1][j] = n + 1;g[rt][have][where] = f[rt][have][where == 0], to = 0;get(rt, 0);if (have == 1)ans = min(ans, g[to][where == 1][2]);elseans = min(ans, min(g[to][where == 1][0], g[to][where == 1][1]));}}if (ans == n + 1)printf("-1");elseprintf("%d\n", ans);
}
int main() {scanf("%d", &n);for (int i = 1, u, v; i <= n; i++)scanf("%d%d", &u, &v), addedge(u, v), addedge(v, u);dfs(1);//先建树for (int i = 1; i <= n; i++)bz[i] = 0;for (int x = 1; x <= n; x++)for (int xy = last[x]; xy; xy = E[xy].next)if (fa[E[xy].to] != x && E[xy].to != fa[x]) {//找环bz[x] = bz[E[xy].to] = 2;change(x, E[xy].to);//标记环上的点solve();return 0;}return 0;
}