做法
我们发现,其实这道题就是要求询问无向图上点对之间的割点数量:可以确定割点的数量是唯一的,因为如果有更多的割点,那么不存在更少的割点使点双连通分量连通。
既然我们知道了大概的题意和要求,那么我们可以发现:如果把这些割点所在域内的非割点全部缩成一个点,然后让割点分别连接,那么根据「不存在更少的割点使点双连通分量连通」的「唯一性」,可以知道这样建出来的东西一定是一棵树。
这样就很好做了!直接树上差分+LCA即可。我们需要用 Tarjan 把这些点缩起来,通过割点进行连接,然后做一遍 DFS 之类的获取前缀和,再处理 LCA 倍增数组,我们就可以在线回答询问了。算法的复杂度为\(\Theta(n \log n + m \log n)\)。
代码
只有 80 分,有三个点被卡了。
// P4320.cpp
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 2e6 + 3;
int head[MAX_N], current, n, m, q, dfn[MAX_N], low[MAX_N], stk[MAX_N], dep[MAX_N];
int ncnt, ptot, tail, cut[MAX_N], aff[MAX_N], dist[MAX_N], org_dist[MAX_N], fa[24][MAX_N];
vector<int> DCCs[MAX_N], G[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
inline char getChar()
{
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) +
fread(buf, 1, 100000, stdin),
p1 == p2)
? EOF
: *p1++;
}
int read()
{
int x = 0, f = 1;
char ch = getChar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getChar();
}
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = getChar();
return x * f;
}
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void tarjan(int u, int org)
{
dfn[u] = low[u] = ++ptot, stk[++tail] = u;
int cnt = 0;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (dfn[edges[i].to] == 0)
{
tarjan(edges[i].to, org), low[u] = min(low[u], low[edges[i].to]);
if (low[edges[i].to] >= dfn[u])
{
// maybe a cut;
cnt++;
if (cnt >= 2 || u != org)
cut[u] = 1;
ncnt++;
do
{
DCCs[ncnt].push_back(stk[tail]), aff[stk[tail]] = ncnt;
} while (stk[tail--] != edges[i].to);
// can't pop the cut out of the stack;
// there must be 2 components connected by the cut;
DCCs[ncnt].push_back(u), aff[u] = ncnt;
}
}
else
low[u] = min(low[u], dfn[edges[i].to]);
}
void bfs(int src)
{
queue<int> q;
q.push(src), dep[src] = 1;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0, siz = G[u].size(); i < siz; i++)
if (dep[G[u][i]] == 0)
{
dep[G[u][i]] = dep[u] + 1, dist[G[u][i]] += dist[u];
fa[0][G[u][i]] = u, q.push(G[u][i]);
}
}
}
int getLCA(int x, int y)
{
if (dep[x] < dep[y])
swap(x, y);
for (int i = 23; i >= 0; i--)
if (dep[fa[i][x]] >= dep[y])
x = fa[i][x];
if (x == y)
return x;
for (int i = 23; i >= 0; i--)
if (fa[i][x] != fa[i][y])
x = fa[i][x], y = fa[i][y];
return fa[0][x];
}
int query(int x, int y)
{
int lca = getLCA(x, y);
return dist[x] + dist[y] - dist[lca] - dist[fa[0][lca]] - org_dist[x] - org_dist[y] + 2;
}
int main()
{
memset(head, -1, sizeof(head));
n = read(), m = read();
for (int i = 1, u, v; i <= m; i++)
u = read(), v = read(), addpath(u, v), addpath(v, u);
// get the DCCs and make it into a fucking tree;
for (int i = 1; i <= n; i++)
if (dfn[i] == 0)
tarjan(i, i);
int tree_tot = ncnt;
for (int i = 1; i <= n; i++)
if (cut[i])
aff[i] = ++tree_tot, dist[aff[i]] = org_dist[aff[i]] = 1;
for (int cp = 1; cp <= ncnt; cp++)
for (int i = 0, siz = DCCs[cp].size(); i < siz; i++)
if (cut[DCCs[cp][i]])
G[cp].push_back(aff[DCCs[cp][i]]), G[aff[DCCs[cp][i]]].push_back(cp);
for (int i = 1; i <= tree_tot; i++)
if (dep[i] == 0)
bfs(i);
for (int i = 1; i <= 23; i++)
for (int j = 1; j <= tree_tot; j++)
fa[i][j] = fa[i - 1][fa[i - 1][j]];
q = read();
while (q--)
{
int x, y;
x = read(), y = read();
printf("%d\n", query(aff[x], aff[y]));
}
return 0;
}
// P4320.cpp
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 2e6 + 3;
int head[MAX_N], current, n, m, q, dfn[MAX_N], low[MAX_N], stk[MAX_N], dep[MAX_N];
int ncnt, ptot, tail, cut[MAX_N], aff[MAX_N], dist[MAX_N], org_dist[MAX_N], fa[24][MAX_N];
vector<int> DCCs[MAX_N], G[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
inline char getChar()
{
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) +
fread(buf, 1, 100000, stdin),
p1 == p2)
? EOF
: *p1++;
}
int read()
{
int x = 0, f = 1;
char ch = getChar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getChar();
}
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = getChar();
return x * f;
}
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void tarjan(int u, int org)
{
dfn[u] = low[u] = ++ptot, stk[++tail] = u;
int cnt = 0;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (dfn[edges[i].to] == 0)
{
tarjan(edges[i].to, org), low[u] = min(low[u], low[edges[i].to]);
if (low[edges[i].to] >= dfn[u])
{
// maybe a cut;
cnt++;
if (cnt >= 2 || u != org)
cut[u] = 1;
ncnt++;
do
{
DCCs[ncnt].push_back(stk[tail]), aff[stk[tail]] = ncnt;
} while (stk[tail--] != edges[i].to);
// can't pop the cut out of the stack;
// there must be 2 components connected by the cut;
DCCs[ncnt].push_back(u), aff[u] = ncnt;
}
}
else
low[u] = min(low[u], dfn[edges[i].to]);
}
void bfs(int src)
{
queue<int> q;
q.push(src), dep[src] = 1;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0, siz = G[u].size(); i < siz; i++)
if (dep[G[u][i]] == 0)
{
dep[G[u][i]] = dep[u] + 1, dist[G[u][i]] += dist[u];
fa[0][G[u][i]] = u, q.push(G[u][i]);
}
}
}
int getLCA(int x, int y)
{
if (dep[x] < dep[y])
swap(x, y);
for (int i = 23; i >= 0; i--)
if (dep[fa[i][x]] >= dep[y])
x = fa[i][x];
if (x == y)
return x;
for (int i = 23; i >= 0; i--)
if (fa[i][x] != fa[i][y])
x = fa[i][x], y = fa[i][y];
return fa[0][x];
}
int query(int x, int y)
{
int lca = getLCA(x, y);
return dist[x] + dist[y] - dist[lca] - dist[fa[0][lca]] - org_dist[x] - org_dist[y] + 2;
}
int main()
{
memset(head, -1, sizeof(head));
n = read(), m = read();
for (int i = 1, u, v; i <= m; i++)
u = read(), v = read(), addpath(u, v), addpath(v, u);
// get the DCCs and make it into a fucking tree;
for (int i = 1; i <= n; i++)
if (dfn[i] == 0)
tarjan(i, i);
int tree_tot = ncnt;
for (int i = 1; i <= n; i++)
if (cut[i])
aff[i] = ++tree_tot, dist[aff[i]] = org_dist[aff[i]] = 1;
for (int cp = 1; cp <= ncnt; cp++)
for (int i = 0, siz = DCCs[cp].size(); i < siz; i++)
if (cut[DCCs[cp][i]])
G[cp].push_back(aff[DCCs[cp][i]]), G[aff[DCCs[cp][i]]].push_back(cp);
for (int i = 1; i <= tree_tot; i++)
if (dep[i] == 0)
bfs(i);
for (int i = 1; i <= 23; i++)
for (int j = 1; j <= tree_tot; j++)
fa[i][j] = fa[i - 1][fa[i - 1][j]];
q = read();
while (q--)
{
int x, y;
x = read(), y = read();
printf("%d\n", query(aff[x], aff[y]));
}
return 0;
}
// P4320.cpp #pragma GCC optimize(2) #pragma GCC optimize("Ofast") #include <bits/stdc++.h> using namespace std; const int MAX_N = 2e6 + 3; int head[MAX_N], current, n, m, q, dfn[MAX_N], low[MAX_N], stk[MAX_N], dep[MAX_N]; int ncnt, ptot, tail, cut[MAX_N], aff[MAX_N], dist[MAX_N], org_dist[MAX_N], fa[24][MAX_N]; vector<int> DCCs[MAX_N], G[MAX_N]; struct edge { int to, nxt; } edges[MAX_N << 1]; inline char getChar() { static char buf[100000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++; } int read() { int x = 0, f = 1; char ch = getChar(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getChar(); } while (isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getChar(); return x * f; } void addpath(int src, int dst) { edges[current].to = dst, edges[current].nxt = head[src]; head[src] = current++; } void tarjan(int u, int org) { dfn[u] = low[u] = ++ptot, stk[++tail] = u; int cnt = 0; for (int i = head[u]; i != -1; i = edges[i].nxt) if (dfn[edges[i].to] == 0) { tarjan(edges[i].to, org), low[u] = min(low[u], low[edges[i].to]); if (low[edges[i].to] >= dfn[u]) { // maybe a cut; cnt++; if (cnt >= 2 || u != org) cut[u] = 1; ncnt++; do { DCCs[ncnt].push_back(stk[tail]), aff[stk[tail]] = ncnt; } while (stk[tail--] != edges[i].to); // can't pop the cut out of the stack; // there must be 2 components connected by the cut; DCCs[ncnt].push_back(u), aff[u] = ncnt; } } else low[u] = min(low[u], dfn[edges[i].to]); } void bfs(int src) { queue<int> q; q.push(src), dep[src] = 1; while (!q.empty()) { int u = q.front(); q.pop(); for (int i = 0, siz = G[u].size(); i < siz; i++) if (dep[G[u][i]] == 0) { dep[G[u][i]] = dep[u] + 1, dist[G[u][i]] += dist[u]; fa[0][G[u][i]] = u, q.push(G[u][i]); } } } int getLCA(int x, int y) { if (dep[x] < dep[y]) swap(x, y); for (int i = 23; i >= 0; i--) if (dep[fa[i][x]] >= dep[y]) x = fa[i][x]; if (x == y) return x; for (int i = 23; i >= 0; i--) if (fa[i][x] != fa[i][y]) x = fa[i][x], y = fa[i][y]; return fa[0][x]; } int query(int x, int y) { int lca = getLCA(x, y); return dist[x] + dist[y] - dist[lca] - dist[fa[0][lca]] - org_dist[x] - org_dist[y] + 2; } int main() { memset(head, -1, sizeof(head)); n = read(), m = read(); for (int i = 1, u, v; i <= m; i++) u = read(), v = read(), addpath(u, v), addpath(v, u); // get the DCCs and make it into a fucking tree; for (int i = 1; i <= n; i++) if (dfn[i] == 0) tarjan(i, i); int tree_tot = ncnt; for (int i = 1; i <= n; i++) if (cut[i]) aff[i] = ++tree_tot, dist[aff[i]] = org_dist[aff[i]] = 1; for (int cp = 1; cp <= ncnt; cp++) for (int i = 0, siz = DCCs[cp].size(); i < siz; i++) if (cut[DCCs[cp][i]]) G[cp].push_back(aff[DCCs[cp][i]]), G[aff[DCCs[cp][i]]].push_back(cp); for (int i = 1; i <= tree_tot; i++) if (dep[i] == 0) bfs(i); for (int i = 1; i <= 23; i++) for (int j = 1; j <= tree_tot; j++) fa[i][j] = fa[i - 1][fa[i - 1][j]]; q = read(); while (q--) { int x, y; x = read(), y = read(); printf("%d\n", query(aff[x], aff[y])); } return 0; }