【题目链接】
【思路要点】
- 建立圆方树,并进行树形DP,求出每个圆点到其子树内最远的圆点的距离\(dp_{i,0}\),以及在不同的子树内距离最远的圆点的距离\(dp_{i,1}\)。
- 考虑枚举直径上离根最近的点:
- 若该点为圆点,那么该圆点对答案的贡献显然为\(dp_{i,0}+dp_{i,1}\)。
- 若该点为方点,那么问题便转化为了“在一个\(N\)个点的环上有一系列点,每个点有权值\(val_i\),现在要选出两个点\(i\),\(j\),使得\(val_i+val_j+dist(i,j)\)最大”。倍长环,转化为序列问题。不妨令\(i<j\)且\(j-i≤\frac{N}{2}\),则要求\(val_i-i+val_j+j\)最大,枚举\(i\),用单调队列维护\(val_j+j\)的最大值即可。
- 时间复杂度\(O(N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } vector <int> a[MAXN], b[MAXN]; int n, m, oldn, ans; int top, Stack[MAXN], len[MAXN]; int timer, dfn[MAXN], low[MAXN]; int dp[MAXN][2]; void dfs(int pos, int fa) { if (pos <= oldn) { for (unsigned i = 0; i < b[pos].size(); i++) if (b[pos][i] != fa) dfs(b[pos][i], pos); } else { int tmp = 0; for (unsigned i = 0; i < b[pos].size(); i++) if (b[pos][i] != fa) { int dest = b[pos][i]; dfs(dest, pos); int tnp = b[pos].size() - 1 - i; chkmax(tmp, dp[dest][0] + min(tnp, len[pos] - tnp)); } if (tmp > dp[fa][0]) { dp[fa][1] = dp[fa][0]; dp[fa][0] = tmp; } else chkmax(dp[fa][1], tmp); } } void tarjan(int pos) { Stack[++top] = pos; dfn[pos] = low[pos] = ++timer; for (unsigned i = 0; i < a[pos].size(); i++) if (dfn[a[pos][i]] == 0) { tarjan(a[pos][i]); chkmin(low[pos], low[a[pos][i]]); if (low[a[pos][i]] >= dfn[pos]) { int tmp = 0, last = 0; n++; while (tmp != a[pos][i]) { last = tmp; tmp = Stack[top--]; b[n].push_back(tmp); b[tmp].push_back(n); len[n]++; } b[n].push_back(pos); b[pos].push_back(n); len[n]++; } } else chkmin(low[pos], dfn[a[pos][i]]); } int main() { read(n), read(m), oldn = n; for (int i = 1; i <= m; i++) { int k; read(k); int pos; read(pos); for (int j = 2; j <= k; j++) { int x; read(x); a[x].push_back(pos); a[pos].push_back(x); pos = x; } } tarjan(1); dfs(1, 0); ans = 0; for (int i = 1; i <= oldn; i++) chkmax(ans, dp[i][0] + dp[i][1]); for (int t = oldn + 1; t <= n; t++) { static int val[MAXN]; int tot = 0, limit = len[t] / 2; for (unsigned i = 0; i < b[t].size(); i++) if (i == b[t].size() - 1) val[++tot] = 0; else val[++tot] = dp[b[t][i]][0]; for (unsigned i = 0; i < b[t].size(); i++) if (i == b[t].size() - 1) val[++tot] = 0; else val[++tot] = dp[b[t][i]][0]; static int q[MAXN]; int l = 0, r = -1; for (int i = 1; i <= limit; i++) { while (l <= r && val[i] + i >= val[q[r]] + q[r]) r--; q[++r] = i; } for (int i = 1; i <= len[t]; i++) { if (q[l] == i) l++; int j = i + limit; while (l <= r && val[j] + j >= val[q[r]] + q[r]) r--; q[++r] = j; chkmax(ans, val[i] + val[q[l]] + q[l] - i); } } writeln(ans); return 0; }