【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=5329
https://loj.ac/problem/2562
【题解】
首先用求点双的方法建出圆方树,那么答案就是虚树中没有被用到的圆点数。
关于如何求点双(圆方树):如果一个点的low不小于父亲的dfn,那么这个点就是割点,新建一个方点。一直弹栈直至把这个点弹出,弹栈的过程中与方点连边。同时父亲也要与方点连边。
其实可以不用建虚树,
就是答案。
时间复杂度
【代码】
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
# define N 400010
# define K 18
using namespace std;
int read(){
int tmp = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9'){tmp = tmp * 10 + ch - '0'; ch = getchar(); }
return tmp * fh;
}
struct Edge{
int data, next;
}e[N * 3];
int head[N], place, use[N], num, ti, n, m, stp[N], top, topp,u[N * 2], v[N * 2], belong[N], low[N], dfn[N],
dad[N][K + 1], ans, dep[N], q, p[N], deg[N];
vector <int> eg[N];
void build(int u, int v){
e[++place].data = v; e[place].next = head[u]; head[u] = place;
}
void tarjan(int x){
use[x] = true; low[x] = dfn[x] = ++ti;
stp[++top] = x;
for (int ed = head[x]; ed != 0; ed = e[ed].next){
if (use[e[ed].data] == false){
tarjan(e[ed].data);
low[x] = min(low[x], low[e[ed].data]);
if (low[e[ed].data] == dfn[x]){
num++;
while (stp[top] != e[ed].data) eg[num].push_back(stp[top--]);
eg[num].push_back(stp[top--]);
eg[num].push_back(x);
}
}
else low[x] = min(low[x], dfn[e[ed].data]);
}
}
void dfs(int x, int fa){
dad[x][0] = fa, deg[x] = (x <= n) + deg[fa]; belong[x] = ++ti; dep[x] = dep[fa] + 1;
for (int ed = head[x]; ed != 0; ed = e[ed].next)
if (e[ed].data != fa && use[e[ed].data] != 2)
dfs(e[ed].data, x);
}
void pre(int n){
for (int i = 1, j = 1; j * 2 <= n; i++, j *= 2)
for (int k = 1; k <= n; k++)
dad[k][i] = dad[dad[k][i - 1]][i - 1];
}
bool cmp(int x, int y){
return belong[x] < belong[y];
}
int lca(int u, int v){
if (dep[u] > dep[v]) swap(u, v);
for (int i = K; i >= 0; i--)
if (dep[dad[v][i]] >= dep[u])
v = dad[v][i];
if (u == v) return u;
for (int i = K; i >= 0; i--)
if (dad[u][i] != dad[v][i])
u = dad[u][i], v = dad[v][i];
return dad[u][0];
}
int main(){
for (int opt = read(); opt--;){
memset(head, 0, sizeof(head));
memset(use, 0, sizeof(use));
place = 0, num = 0, ti = 0, top = 0;
n = read(), m = read();
for (int i = 1; i <= m; i++){
u[i] = read(), v[i] = read();
build(u[i], v[i]); build(v[i], u[i]);
}
tarjan(1);
memset(head, 0, sizeof(head)); place = 0; ti = 0;
for (int i = 1; i <= num; i++){
int tmp = i + n;
if (eg[i].size() == 2){
build(eg[i][0], eg[i][1]), build(eg[i][1], eg[i][0]);
}
else {
for (unsigned j = 0; j < eg[i].size(); j++)
build(eg[i][j], tmp), build(tmp, eg[i][j]);
}
eg[i].clear();
}
memset(dad, 0, sizeof(dad));
ti = 0; dfs(1, 0);
pre(n + num);
q = read();
for (int i = 1; i <= q; i++){
num = read(), ans = -2 * num;
for (int j = 1; j <= num; j++) p[j] = read();
sort(p + 1, p + num + 1, cmp);
p[num + 1] = p[1];
for (int j = 1; j <= num; j++){
int tmp = lca(p[j], p[j + 1]);
ans = ans + deg[p[j]] + deg[p[j + 1]] - 2 * deg[tmp];
}
ans /= 2;
int tmp = lca(p[1], p[num]);
if (deg[tmp] != deg[dad[tmp][0]]) ans++;
printf("%d\n", ans);
}
}
return 0;
}