题目链接:传送门
思路:
题意说用线段的相交作为边,来构造树,所以不存在大于等于3个的线段两两相交,否则会构成环。因而构造出的树中,每个点最多只会与2个度大于1的节点相邻。
不妨把1设为树根,用degu表示原树中节点u的度,ans表示答案。
用fu表示:假设以u为根的子树,已经有一条边连向了一个度大于1的点时,所能构成的最大的“子树的子树”的大小,则有:
fu = 1,if degu=1。叶子本身就是一个点,大小为1。
fu = max{v是u的子节点 | fv} + degu-2 + 1。所有孩子中选一个fv最大的作为第2个度大于1的节点(第1个已经连出去了),这时相邻的点已经用掉2个了,剩余其他的所有相邻的点的数量为degu-2,再算上u本身。
这样处理完之后答案就很好算了。
对于一个节点u的子节点v中,fv最大的两个之和+degu-2+1,就是答案的一种可能。
(对答案的更新,代码中不是这样写的。我在更新答案的时候,利用了fu已经保存之前(fv+degu-2+1)的最大值相关信息的特点,直接用fu来更新答案了)
代码实现:O(N)
#include <bits/stdc++.h> #define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define N 300005 #define M 300005 #define INF 0x3f3f3f3f #define mk(x) (1<<x) // be conscious if mask x exceeds int #define sz(x) ((int)x.size()) #define upperdiv(a,b) (a/b + (a%b>0)) #define mp(a,b) make_pair(a, b) #define endl ‘\n‘ #define lowbit(x) (x&-x) using namespace std; typedef long long ll; typedef double db; /** fast read **/ template <typename T> inline void read(T &x) { x = 0; T fg = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == ‘-‘) fg = -1; ch = getchar(); } while (isdigit(ch)) x = x*10+ch-‘0‘, ch = getchar(); x = fg * x; } template <typename T, typename... Args> inline void read(T &x, Args &... args) { read(x), read(args...); } int tot = 0; int head[N], nxt[M<<1], ver[M<<1], deg[N]; void addEdge(int u, int v) { nxt[++tot] = head[u], ver[tot] = v, head[u] = tot; deg[u]++; } int f[N]; bool vis[N]; int ans; void dfs(int u) { vis[u] = true; f[u] = deg[u]; for (int i = head[u]; i != -1; i = nxt[i]) { int v = ver[i]; if (vis[v]) continue; dfs(v); ans = max(ans, f[u] + f[v]); f[u] = max(f[u], f[v] + deg[u]-2 + 1); } } int main() { int q; read(q); while (q--) { int n; read(n); ans = 0; tot = 0; for (int i = 1; i <= n; i++) { head[i] = -1; deg[i] = 0; f[i] = 0; vis[i] = false; } int u, v; for (int i = 1; i <= n-1; i++) { read(u, v); addEdge(u, v); addEdge(v, u); } dfs(1); cout << ans << endl; } return 0; }
原文地址:https://www.cnblogs.com/Lubixiaosi-Zhaocao/p/11701465.html
时间: 2024-10-13 00:52:12