题目大意:
有一个n个点m条边的无向图,一共Q次询问,每次询问一个x,要
求回答:若去掉编号为x的边,会有多少个点不能互相到达?
(结果mod 1000)
解题思路
1. tarjan算法:
(1) 求出桥,并记录下来
(2) 求每个点所在的连通块的个数
(3) 求每个点在dfs树中有多少个子节点
(4) 求原本有多少个点不能互相到达 (这里标记为k)
2.求答案 :
(1) 判断是否为桥
(2) 若为桥:求出此边把所在的dfs树分成的两块的节点个数(记为a, b) 答案为a * b + k
(3) 若不是:答案为k
代码环节:
#include <cstdio>
#include <iostream>
#include <iostream>
using namespace std;
const int maxn = 1000010 ;
int mod = 2000;
struct node {
int from, to, nxt;
} edge[maxn << 1];
int n, m, q, k, t, cnt = 1, cmp;
int dfn[maxn], low[maxn], vis[maxn], num[maxn], sum[maxn], first[maxn], bridge[maxn];
void add (int u, int v) {
cnt ++;
edge[cnt].to = v;
edge[cnt].from = u;
edge[cnt].nxt = first[u];
first[u] = cnt;
}
void tarjan (int u, int com) {
t ++; cmp ++;
num[u] = 1;
dfn[u] = low[u] = t;
for (int i = first[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (i == (com ^ 1)) continue;
if (!dfn[v]) {
tarjan (v, i);
low[u] = min (low[u], low[v]);
num[u] += num[v];
if (low[v] > dfn[u]) {
bridge[i / 2 - 1] = 1;
}
} else {
low[u] = min (low[u], dfn[v]);
}
}
}
void dfs (int now) {
sum[now] = cmp;
vis[now] = 1;
for (int i = first[now]; i; i = edge[i].nxt) {
int use = edge[i].to;
if (!vis[use]) dfs (use);
}
}
int main() {
int num1 = 0, num2 = 0;
scanf ("%d%d%d", &n, &m, &q);
for (int i = 1; i <= m; ++i) {
int u, v; scanf ("%d%d", &u, &v);
add (u, v); add (v, u);
}
for (int i = 0; i < n; ++i) {
cmp = 0;
if (!dfn[i]) {
tarjan (i, 0);
dfs (i);
}
cmp %= mod;
num1 = (num1 + cmp) % mod;
num2 = (num2 + (cmp * cmp) % mod) % mod;
}
k = ((num1 * num1) % mod - num2 % mod + mod) % mod / 2;
mod = 1000;
while (q --) {
int x; scanf ("%d", &x);
if (!bridge[x]) {
printf ("%d\n", k);
continue;
}
int a = min (num[edge[(x + 1) * 2].from], num[edge[(x + 1) * 2].to]);
int b = sum[edge[(x + 1) * 2].to];
int ans = ((a % mod) * ((b - a) % mod)) % mod;
printf ("%d\n", (ans + k) % mod);
}
return 0;
}