前置技能点:LCT,双联通分量
如果你不知道上面的东西,请先行了解
start_of_题面
对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系。
星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1、2、3……。
一些先遣飞船已经出发,在星球之间开辟探险航线。
探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到1号星球也可以使用这条航线。
例如下图所示:
在5个星球之间,有5条探险航线。
A、B两星球之间,如果某条航线不存在,就无法从A星球抵达B星球,我们则称这条航线为关键航线。
显然上图中,1号与5号星球之间的关键航线有1条:即为4-5航线。
然而,在宇宙中一些未知的磁暴和行星的冲撞,使得已有的某些航线被破坏,随着越来越多的航线被破坏,探险飞船又不能及时回复这些航线,可见两个星球之间的关键航线会越来越多。
假设在上图中,航线4-2(从4号星球到2号星球)被破坏。此时,1号与5号星球之间的关键航线就有3条:1-3,3-4,4-5。
小联的任务是,不断关注航线被破坏的情况,并随时给出两个星球之间的关键航线数目。现在请你帮助完成。
Input
第一行有两个整数N,M。表示有N个星球(1< N < 30000),初始时已经有M条航线(1 < M < 100000)。随后有M行,每行有两个不相同的整数A、B表示在星球A与B之间存在一条航线。接下来每行有三个整数C、A、B。C为1表示询问当前星球A和星球B之间有多少条关键航线;C为0表示在星球A和星球B之间的航线被破坏,当后面再遇到C为1的情况时,表示询问航线被破坏后,关键路径的情况,且航线破坏后不可恢复; C为-1表示输入文件结束,这时该行没有A,B的值。被破坏的航线数目与询问的次数总和不超过40000。
Output
对每个C为1的询问,输出一行一个整数表示关键航线数目。 注意:我们保证无论航线如何被破坏,任意时刻任意两个星球都能够相互到达。在整个数据中,任意两个星球之间最多只可能存在一条直接的航线。
Sample Input
5 5
1 2
1 3
3 4
4 5
4 2
1 1 5
0 4 2
1 5 1
-1
Sample Output
1
3
end_of_题面
假设我们已经完全清楚了LCT和边双联通分量的基本性质和作用
看到删边先想两个东西——并查集和LCT,并查集虽然不支持删边,但是离线把操作反过来就变成了加边,LCT就不解释了。
然后一看要维护链上信息,并查集就很难搞了,基本确定要用LCT。
发现即使是LCT,题目要维护的信息在删边的情况下也不是很好做,所以我们把操作反过来,这样删边变成加边,查询还是查询。现在问题变成了加一条边对答案有什么影响。接下来上我的灵魂绘图
我们发现如果我们有一棵树,我们在两点之间连一条边,那么这两点之间的点和边会组成一个边双联通分量,这些边都会变得不符合题目中所说关键航线的性质,我们可以直接把它们缩成一个点。这样的话,我们在查询的时候只需要查询链有多长就可以了,这个东西LCT可以随便维护。
题目基本解决,细节不清楚的可以看代码
start_of_code
#include <map>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 30000 + 3000;
const int O = 40000 + 4000;
const int M = 100000 + 1000;
map< pair<int, int>, int > p;
pair<int, int> us;
int n, m, ans[O];
struct op{
int fc, x, y;
}o[O];
struct edge{
int x, y;
}e[M];
inline int read()
{
int a = 0;
char ch;
int f = 1;
while(!((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-')));
if(ch == '-')
f = -1;
else
{
a = a * 10;
a += ch - '0';
}
while((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-'))
{
a = a * 10;
a += ch - '0';
}
return a * f;
}
namespace LCT{
int color[N], son[N][2], fa[N], rev[N], sum[N], father[N];
inline void init()
{
for(int i = 1;i <= n;++i)
father[i] = i, color[i] = i;
}
inline int find(int x)
{
if(father[x] != x)
return father[x] = find(father[x]);
return x;
}
inline int findc(int x)
{
if(color[x] != x)
return color[x] = findc(color[x]);
return x;
}
inline int which(int x)
{
return x == son[fa[x]][1];
}
inline bool isroot(int x)
{
return x != son[fa[x]][0] && x != son[fa[x]][1];
}
inline void pushdown(int x)
{
if(rev[x])
{
rev[son[x][0]] ^= 1;
rev[son[x][1]] ^= 1;
swap(son[x][0], son[x][1]);
rev[x] = 0;
}
}
inline void update(int x)
{
sum[x] = sum[son[x][0]] + sum[son[x][1]] + 1;
}
inline void rotate(int x)
{
int f = fa[x], g = fa[f], d = which(x);
if(!isroot(f))
son[g][which(f)] = x;
son[f][d] = son[x][d ^ 1];
fa[son[f][d]] = f;
son[x][d ^ 1] = f;
fa[f] = x;
fa[x] = g;
update(f);
update(x);
}
int stk[N], top = 0;
inline void splay(int x)
{
top = 0;
stk[++top] = x;
int fc;
for(int i = x;!isroot(i);i = findc(fa[i]))
stk[++top] = findc(fa[i]);
for(int i = top;i >= 1;--i)
pushdown(stk[i]), fa[stk[i]] = findc(fa[stk[i]]);
for(;!isroot(x);rotate(x))
if(!isroot(fa[x]))
rotate(which(x) == which(fa[x]) ? fa[x] : x);
}
inline void access(int x)
{
int y = 0;
for(;x;y = x, x = findc(fa[x]))
splay(x), son[x][1] = y, update(x);
}
inline void makeroot(int x)
{
access(x);
splay(x);
rev[x] ^= 1;
}
inline void link(int x, int y)
{
makeroot(x);
fa[x] = y;
splay(x);
}
inline void split(int x, int y)
{
makeroot(x);
access(y);
splay(y);
}
inline void Uni(int x, int y)
{
color[findc(x)] = findc(y);
pushdown(x);
if(son[x][0])
Uni(son[x][0], y);
if(son[x][1])
Uni(son[x][1], y);
son[x][0] = son[x][1] = 0;
}
}
using namespace LCT;
int main()
{
n = read(), m = read();
for(int i = 1;i <= m;++i)
e[i].x = read(), e[i].y = read();
init();
int tot = 1;
while(true)
{
o[tot].fc = read();
if(o[tot].fc == -1)
{
--tot;
break;
}
o[tot].x = read(), o[tot].y = read();
if(o[tot].fc == 0)
{
us.first = o[tot].x;
us.second = o[tot].y;
p[us] = 1;
}
++tot;
}
for(int i = 1;i <= m;++i)
{
if(p[make_pair(e[i].x, e[i].y)] != 1)
{
int fx = findc(e[i].x), fy = findc(e[i].y);
if(fx == fy)
continue;
int fa = find(fx), fb = find(fy);
if(fa != fb)
{
father[fb] = fa;
link(fx, fy);
}
else
{
split(fx, fy);
Uni(fy, fy);
update(fy);
}
}
}
for(int i = tot;i >= 1;--i)
{
if(o[i].fc == 0)
{
int fx = findc(o[i].x), fy = findc(o[i].y);
if(fx == fy)
continue;
int fa = find(fx), fb = find(fy);
if(fa != fb)
{
father[fb] = fa;
link(fx, fy);
}
else
{
split(fx, fy);
Uni(fy, fy);
update(fy);
}
}
else
{
int fx = findc(o[i].x), fy = findc(o[i].y);
split(fx, fy);
ans[i] = sum[fy] - 1;
}
}
for(int i = 1;i <= tot;++i)
if(o[i].fc == 1)
printf("%d\n", ans[i]);
return 0;
}