【bzoj4998】星球联盟——LCT维护双联通分量

前置技能点:LCT,双联通分量

如果你不知道上面的东西,请先行了解

start_of_题面

Description

在遥远的S星系中一共有 N N N个星球,编号为 1 … N 1…N 1N。其中的一些星球决定组成联盟,以方便相互间的交流。但是,组成
联盟的首要条件就是交通条件。初始时,在这 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 1N,M,P200000

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写得好长啊…

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值