【题目链接】
【思路要点】
- 建立原图的圆方树。
- 对于每个询问,建立其询问点集的虚树,分别考虑虚树的每一个点和每一条边上的圆点是否能够作为答案。
- 简单树形DP即可。
- 时间复杂度\(O(T(N+\sum S)LogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 200005; const int MAXLOG = 20; 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(""); } struct edge {int len, dest; }; int oldn, n, m, q, top, Stack[MAXN]; int timer, dfn[MAXN], low[MAXN]; int ans, k, pos[MAXN], size[MAXN]; bool selected[MAXN]; int depth[MAXN], dist[MAXN], father[MAXN][MAXLOG]; vector <int> a[MAXN], b[MAXN]; vector <edge> c[MAXN]; void dfs(int pos, int fa) { dfn[pos] = ++timer; father[pos][0] = fa; depth[pos] = depth[fa] + 1; dist[pos] = dist[fa] + (pos <= oldn); for (int i = 1; i < MAXLOG; i++) father[pos][i] = father[father[pos][i - 1]][i - 1]; for (unsigned i = 0; i < b[pos].size(); i++) if (b[pos][i] != fa) dfs(b[pos][i], pos); } void work(int pos) { bool tans = false; for (unsigned i = 0; i < c[pos].size(); i++) { work(c[pos][i].dest); size[pos] += size[c[pos][i].dest]; if (size[c[pos][i].dest] != 0 && size[c[pos][i].dest] != k) ans += c[pos][i].len; tans |= size[c[pos][i].dest] != 0 && size[c[pos][i].dest] != k; } ans += tans && !selected[pos] && pos <= oldn; } 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 = Stack[top--]; b[++n].push_back(tmp); b[tmp].push_back(n); while (tmp != a[pos][i]) { tmp = Stack[top--]; b[n].push_back(tmp); b[tmp].push_back(n); } b[n].push_back(pos); b[pos].push_back(n); } } else chkmin(low[pos], dfn[a[pos][i]]); } int lca(int x, int y) { if (depth[x] < depth[y]) swap(x, y); for (int i = MAXLOG - 1; i >= 0; i--) if (depth[father[x][i]] >= depth[y]) x = father[x][i]; if (x == y) return x; for (int i = MAXLOG - 1; i >= 0; i--) if (father[x][i] != father[y][i]) { x = father[x][i]; y = father[y][i]; } return father[x][0]; } bool cmp(int x, int y) { return dfn[x] < dfn[y]; } int main() { int T; read(T); while (T--) { read(n), read(m), oldn = n; for (int i = 1; i <= n * 2; i++) a[i].clear(), b[i].clear(); for (int i = 1; i <= m; i++) { int x, y; read(x), read(y); a[x].push_back(y); a[y].push_back(x); } memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); timer = top = 0; tarjan(1); timer = 0; memset(dfn, 0, sizeof(dfn)); dfs(1, 0); read(q); while (q--) { static int used[MAXN]; int tot = 0; read(k); for (int i = 1; i <= k; i++) { read(pos[i]); size[pos[i]] = 1; selected[pos[i]] = true; } sort(pos + 1, pos + k + 1, cmp); Stack[top = 1] = 1; used[tot = 1] = 1; for (int i = 1; i <= k; i++) { if (pos[i] == Stack[top]) continue; int Lca = lca(pos[i], Stack[top]); if (Lca == Stack[top]) { used[++tot] = pos[i]; Stack[++top] = pos[i]; continue; } while (top >= 2 && dfn[Lca] < dfn[Stack[top - 1]]) { int x = Stack[top], y = Stack[--top]; c[y].push_back((edge) {dist[x] - dist[y] - (x <= oldn), x}); } int x = Stack[top--], y = Lca; c[y].push_back((edge) {dist[x] - dist[y] - (x <= oldn), x}); if (Lca != Stack[top]) { Stack[++top] = Lca; used[++tot] = Lca; } Stack[++top] = pos[i]; used[++tot] = pos[i]; } while (top >= 2) { int x = Stack[top], y = Stack[--top]; c[y].push_back((edge) {dist[x] - dist[y] - (x <= oldn), x}); } ans = 0; work(1); for (int i = 1; i <= tot; i++) { size[used[i]] = 0; selected[used[i]] = false; c[used[i]].clear(); } writeln(ans); } } return 0; }