前置技能点:LCT,双联通分量
如果你不知道上面的东西,请先行了解
start_of_题面
Description
在遥远的S星系中一共有
N
N
N个星球,编号为
1
…
N
1…N
1…N。其中的一些星球决定组成联盟,以方便相互间的交流。但是,组成
联盟的首要条件就是交通条件。初始时,在这
N
N
N个星球间有
M
M
M条太空隧道。每条太空隧道连接两个星球,使得它们能
够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有
公共隧道的路径。为了壮大联盟的队伍,这些星球将建设
P
P
P条新的太空隧道。这
P
P
P条新隧道将按顺序依次建成。一条
新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨
道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。
Input
第1行三个整数
N
N
N,
M
M
M和
P
P
P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。
第2至第
M
+
1
M+1
M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。
第
M
+
2
M+2
M+2行至第
M
+
P
+
1
M+P+1
M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。
这些太空隧道按照输入的顺序依次建成。
1
≤
N
,
M
,
P
≤
200000
1 \le N,M,P \le 200000
1≤N,M,P≤200000
Output
输出共
P
P
P行。
如果这条新的太空隧道连接的两个星球属于同一个联盟,就输出一个整数,表示这两个星球所在联盟的星球数。
如果这条新的太空隧道连接的两个星球不属于同一个联盟,就输出"No"(不含引号)。
Sample Input
5 3 4
1 2
4 3
4 5
2 3
1 3
4 5
2 4
Sample Output
No
3
2
5
Hint
end_of_题面
分析基本和bzoj1969相同,这里就不详细写了,可以看这里
唯一的区别就是bzoj1969在缩完点以后就当做一个点看,但是这道题需要保留边双联通分量的大小,随便搞搞就好了
start_of_code
#include <map>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 200000 + 2000;
int n, m, p, x, y, ss = 0;
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], val[N];
inline void init()
{
for(int i = 1;i <= n;++i)
father[i] = i, color[i] = i, val[i] = 1;
}
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]] + val[x];
}
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);
ss += val[x];
if(x != y)
val[y] += val[x], val[x] = 0;
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(), p = read();
init();
for(int i = 1;i <= m;++i)
{
x = read(), y = read();
int fx = findc(x), fy = findc(y);
if(fx == fy)
continue;
int fa = find(fx), fb = find(fy);
if(fa != fb)
{
father[fb] = fa;
link(fx, fy);
}
else
{
ss = 0;
split(fx, fy);
Uni(fy, fy);
}
}
for(int i = 1;i <= p;++i)
{
x = read(), y = read();
int fx = findc(x), fy = findc(y);
if(fx == fy)
{
printf("%d\n", val[fy]);
continue;
}
int fa = find(fx), fb = find(fy);
if(fa != fb)
{
father[fb] = fa;
link(fx, fy);
printf("No\n");
}
else
{
ss = 0;
split(fx, fy);
Uni(fy, fy);
printf("%d\n", ss);
}
}
return 0;
}
end_of_code
PS:突然觉得我的LCT写得好长啊…