并查集进阶

并查集进阶

先假设读者都已经会最基本的并查集了(模板&最小生成树)

1.维护元素间的“不同”关系

例题:传送门
题意:城市里有两个黑帮,虎帮\和龙帮。现在有 n ( n ≤ 1 0 5 ) n(n\leq10^5) n(n105)个罪犯,不是在龙帮就是在虎帮。已知 m ( m ≤ 1 0 5 ) m(m\leq 10^5) m(m105)个关系以如下方式描述:(a,b表示两个人的编号)

  • D a,b:a,b属于不同的帮派
  • A a,b:询问a,b间的关系:
    • 同一帮派:输出"In the same gang."
    • 不同帮派:输出"In different gangs."
    • 目前还不确定:输出"Not sure yet."

T ( T ≤ 20 ) T(T\leq 20) T(T20)组询问

题解:考虑并查集:find(u)==find(v):u,v在同一个帮派中
另建立一个数组p[i],表示i有一个敌人是p[i]
有个小问题:为什么只要保存一个敌人?因为i的敌人在同一个集合中。
合并的时候

  • 如果a还没有确定敌人,令p[a]=b
  • 如果a已经确定了敌人,合并p[a],b

b同理。
最后关系的判断可见代码:

/**********/
#define MAXN 100011
struct ufs//封装良好的并查集,给个赞呗
{
	ll fa[MAXN];
	void build(ll n)
	{
		for(ll i=1;i<=n;++i)fa[i]=i;
	}
	ll find(ll x)
	{
		if(fa[x]==x)return x;
		return fa[x]=find(fa[x]);
	}
	void uni(ll u,ll v)
	{
		u=find(u),v=find(v);
		if(u==v)return;
		fa[u]=v;
	}
	bool same(ll u,ll v)
	{
		u=find(u),v=find(v);
		return u==v;
	}
}s;
ll p[MAXN];
void work()
{
	ll n=read(),m=read();//快读被省略
	s.build(n);
	memset(p,0,sizeof p);
	for(ll i=1;i<=m;++i)
	{
		char c=getchar();
		while(c!='A'&&c!='D')c=getchar();
		ll u=read(),v=read();
		if(c=='A')
		{
			if(s.same(u,v))puts("In the same gang.");
			else if((p[u]&&s.same(p[u],v))||(p[v]&&s.same(p[v],u)))puts("In different gangs.");//敌人与对方在同一集合,说明自己和对方是不同集合(帮派)
			else puts("Not sure yet.");
		}
		else
		{
			if(p[u])s.uni(p[u],v);
			else p[u]=v;
			if(p[v])s.uni(p[v],u);
			else p[v]=u;
		}
	} 
}
int main()
{
	ll t=read();
	while(t--)work();
	return 0;
}
小结:由此可见,可以并查集通过维护一个“不同的”对象完成维护元素间的“不同”关系

2.维护分离关系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值