CF1338D Nested Rubber Bands

CF1338D Nested Rubber Bands

玩了一下样例,以为是最大独立集板子,交了一发第 \(4\) 个点就错了(笑)

仔细钻研了一下错的数据,发现一个点如果在 \(3\) 个子树内都有选点就会出错。

再钻研一下发现不对,菊花图显然不满足这个判断。为什么菊花图有问题呢?发现原来菊花图属于当前点 \(u\) 不选但所有 \(v\) 能选上的情况。那再试着推广一下这个情况。

于是接着发现一个性质:在一棵一般的树中,对于一个 \(u\),如果 \(u\) 不选,并且选了 \(3\) 个以上的直接连点比如 \(x, y, z\),那么 \(x, y, z\) 所在的子树中就 不可能再选别的点了(画一画图就知道一定会和 \(u\) 交叉)

所以进一步总结:问题转化为选一条链和所有与连直接相连的点,求这个子集的最大独立集。

所以本题是一道最大独立集的变式,肯定还是考虑树形 DP。

由上述分析可知,状态定义的关键就在于当前点 \(u\) 在不在序列里面,这和最大独立集类似。

对于一个点 \(i\),我们用 \(f_{i, 0}\) 表示在以 \(i\) 为根的子树中,\(i\) 作为链的一端,并且 \(i\) 不在序列中,序列的最大长度。\(f_{i, 1}\) 就是 \(i\) 在序列中的情况。

转移:

\[ f_{v, 1} + d_u - 2 \to f_{u, 0} \\ f_{v, 0} + d_u - 2 \to f_{u, 0} \\ f_{v, 0} + 1 \to f_{u, 1} \\ \]

\(d_u - 2\) 可以理解成针对局部是菊花图的情况。(或者说其实就是考虑和链直接相连的点的贡献)

边转移边更新:(人话就是把两条链拼起来,注意此时 \(f_{u}\) 都是用 \(f_{v}\) 转移之前的状态)

\[ f_{u, 0} + f_{v, 0} \to ans \\ f_{u, 0} + f_{v, 1} \to ans \\ f_{u, 1} + f_{v, 0} \to ans \\ \]

最后再更新:

\[ f_{u, 1} \to ans \]

时间复杂度 \(O(n)\)


#include<bits/stdc++.h>

#define F(i,l,r) for(int i(l); i <= (r); ++ i)

#define G(i,r,l) for(int i(r); i >= (l); -- i)

using namespace std;

using ll = long long;

const int N = 1e5 * 2;

vector<int> G[N];

int f[N][5];

int n, ans = 0;

void dfs(int u, int fa){

	int d = G[u].size() - 2;

	f[u][1] = 1;

	f[u][0] = d;

	for(auto v : G[u]){

		if(v == fa) continue;

		dfs(v, u);

		ans = max(ans, f[u][0] + f[v][0]);

		ans = max(ans, f[u][0] + f[v][1]);

		ans = max(ans, f[u][1] + f[v][0]);

		f[u][0] = max(f[u][0], max(f[v][1] + d, f[v][0] + d));

		f[u][1] = max(f[u][1], f[v][0] + 1);

	}

	ans = max(ans, f[u][1]);

}

signed main(){

	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

	cin >> n;

	F(i, 1, n - 1){

		int u, v;

		cin >> u >> v;

		G[u].push_back(v);

		G[v].push_back(u);

	}

	dfs(1, 0);

	cout << ans << '\n';

	return fflush(0), 0;

}

posted @ 2025-07-30 16:42  superl61  阅读(9)  评论(0)    收藏  举报