题目传送门:POJ 3694

# 1. 题目大意及思路

给你一个图,有 Q 个操作,每个操作将点 u 和 v 之间连一条边,问你在每次加边之后图中的割边的数量。

分析:首先用 Tarjan 在加边前求一次割边的数量,并用并查集缩点,缩点之后的图将会变成一棵树。加边时,任意两点之间直接暴力求 LCA,在求 LCA 的过程更新割边的数量。因为一棵树加上一条边之后必定有环,那么就割边的数量就会减少。具体实现看代码。


# 2. 解题代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100005
using namespace std;

struct node
{
int v,next;
}edge[N<<2];

int pre[N],cnt;
int dfn[N],low[N],id;
int fa[N],f[N];
int n,m,q,cut;

int Scan()
{
int f = 1,res = 0;
char ch;
while((ch = getchar())==' ' || ch == '\n');
if(ch == '-')f = -1;
while(ch >= '0' && ch <= '9')
{
res = res * 10 + ch - '0';
ch = getchar();
}
return f * res;
}

void init()
{
cnt = id = cut = 0;
memset(pre, -1, sizeof(pre));
memset(dfn, 0, sizeof(dfn));
for(int i = 0; i <= n; ++i)
f[i] = i;
}

void addEdge(int u, int v)
{
edge[cnt].v = v;
edge[cnt].next = pre[u];
pre[u] = cnt++;
}

int _find(int x)
{
int xx = x;
while(x != f[x])
x = f[x];
while(xx != x)
{
int u = f[xx];
f[xx] = x;
xx = u;
}
return x;
}

void _union(int x, int y)
{
f[_find(y)] = _find(x);
}

void Tarjan(int u)
{
dfn[u] = low[u] = ++id;
for(int i = pre[u]; ~i; i=edge[i].next)
{
int v = edge[i].v;
if(!dfn[v])
{
fa[v] = u;
Tarjan(v);
if(low[v] > dfn[u])
cut++;
else _union(u, v);
low[u] = min(low[u], low[v]);
}
else if(v != fa[u])
low[u] = min(low[u], dfn[v]);
}
}

void LCA(int u, int v)
{
if(dfn[u] > dfn[v])
swap(u, v);
int x = _find(u);
while(dfn[v] > dfn[u])
{
int y = _find(v);
if(x != y)cut--;
f[y] = x;
v = fa[v];
}
int y = _find(v);
while(u != v)
{
int x = _find(u);
if(y != x)cut--;
f[x] = y;
u = fa[u];
}
printf("%d\n", cut);
}

int main()
{
int cas = 0;
while(~scanf("%d%d", &n,&m), n&&m)
{
init();
int u,v;
while(m--)
{
u = Scan(),v = Scan();
addEdge(u, v);
addEdge(v, u);
}
Tarjan(1);
printf("Case %d:\n", ++cas);
q = Scan();
while(q--)
{
u = Scan(),v = Scan();
LCA(u, v);
}
printf("\n");
}
return 0;
}
更新于

请我喝[茶]~( ̄▽ ̄)~*

Revincent 微信

微信

Revincent 支付宝

支付宝