本质上还是树形dp。建立圆方树,遇到圆点的时候直接求(和树形dp一样即可),遇到方点做中转点的时候要考虑会从圆的另一侧通过(需满足最短路径的原则)。原本是对于圆上的点进行 \(n^{2}\) 的匹配,果断超时。但没有发现 \(n ^ {2}\) 的dp明显是一个可以单调队列优化的dp。所以在遇上难解决的问题的时候,一定要融会贯通地思考。有一个细节:将圆复制一下可以去掉 \(max\)造成的影响,dp就十分方便了。
#includeusing namespace std; #define maxn 500000 int n, m, N, timer, ans; int fa[maxn], dfn[maxn], low[maxn]; int f[maxn][2], id[maxn], S[maxn]; int tmp[maxn], q[maxn]; int F[maxn][2], SS[maxn]; struct edge { int cnp, head[maxn], to[maxn], last[maxn], w[maxn]; edge() { cnp = 1; } void add(int u, int v, int ww) { to[cnp] = v, last[cnp] = head[u], w[cnp] = ww, head[u] = cnp ++; to[cnp] = u, last[cnp] = head[v], w[cnp] = ww, head[v] = cnp ++; } }E1, E2; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } void Solve(int u, int v) { ++ N; int ID = 0, pre = 1; for(int i = v; i != fa[u]; i = fa[i]) S[i] = pre ++, id[i] = ++ ID; S[N] = S[u], S[u] = 0; for(int i = v; i != fa[u]; i = fa[i]) E2.add(N, i, min(S[i], S[N] - S[i])); } void Tarjan(int u) { dfn[u] = low[u] = ++ timer; for(int i = E1.head[u]; i; i = E1.last[i]) { int v = E1.to[i]; if(v == fa[u]) continue; if(!dfn[v]) { fa[v] = u; Tarjan(v); low[u] = min(low[u], low[v]); } else low[u] = min(low[u], dfn[v]); if(low[v] > dfn[u]) E2.add(u, v, 1); } for(int i = E1.head[u]; i; i = E1.last[i]) { int v = E1.to[i]; if(dfn[u] < dfn[v] && fa[v] != u) Solve(u, v); } } void update(int u, int w) { if(w <= f[u][1]) return; if(w < f[u][0]) f[u][1] = w; else f[u][1] = f[u][0], f[u][0] = w; } bool cmp(int a, int b) { return id[a] < id[b]; } int Work(int u) { int head = 1, tail = 1, cnt = 1, ans = 0, len = 0; for(int i = E2.head[u]; i; i = E2.last[i]) tmp[++ cnt] = f[E2.to[i]][0], len ++; for(int i = 1; i <= len; ++ i) tmp[++ cnt] = tmp[i]; q[head] = 1; for(int i = 2; i <= cnt; ++ i) { while(head < tail && i - q[head] > len / 2) ++ head; ans = max(ans, tmp[i] + tmp[q[head]] + i - q[head]); while(head <= tail && tmp[i] - i > tmp[q[tail]] - q[tail] ) -- tail; q[++ tail] = i; } return ans; } void dfs(int u, int ff) { for(int i = E2.head[u]; i; i = E2.last[i]) { int v = E2.to[i]; if(v == ff) continue; else dfs(v, u); update(u, f[v][0] + E2.w[i]); } if(u <= n) ans = max(ans, f[u][1] + f[u][0]); else ans = max(ans, Work(u)); } int main() { N = n = read(), m = read(); for(int i = 1; i <= m; i ++) { int tot = read(), u, pre; for(int j = 1; j <= tot; j ++) { u = read(); if(j == 1) { pre = u; continue; } E1.add(u, pre, 1); pre = u; } } Tarjan(1); dfs(1, 0); printf("%d\n", ans); return 0; }