分类并查集

P2024

题目大意:给出x是y的同类,x吃y两种关系,求出他们的关系假话总数

思路:维护两个元素的关系较复杂,需要一个元素维护3个部分

A:该元素与它的同类

B:该元素与它的猎物

C:该元素与它的天敌

判断一句话是否为假:

1.xy是同类:是否有吃与被吃,天敌与是天敌的关系

即是Bx!=AyCx!=Ay

添加关系:一对一添加

Ay->Ax

By->Bx

Cy->Cx

2.x吃y:是否有为同类,y吃x(y是x的天敌)的关系

添加关系:

Ay->Bx  y的同类作为x的猎物

By->Cx  y的猎物作为x的天敌

Cy->Ax   y的天敌作为x的同类

#include<bits/stdc++.h>

using namespace std;

const int N = 2e6 + 10;
int n,m,ans,fa[N];


inline int find(int x){
	if(fa[x]!=x)  return fa[x]=find(fa[x]);
	else return x;
}
inline void update(int x,int y){
	if(find(x)!=find(y))
	  fa[find(x)]=find(y);
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')  f=-1;
		ch=getchar();
	}while(ch>='0'&&ch<='9'){
		x=x*10+ch-48;ch=getchar();
	}return x*f;
}
int main(){
	//x是同类 x+n是猎物 x+2*n是天敌 
	n=read();m=read();
	for(int i=1;i<=3*n;i++)  fa[i]=i;
    for(int i=1,lx,x,y;i<=m;i++){
    	lx=read();x=read();y=read();
    	if(x>n||y>n||(lx==2&&x==y)){
    		ans++;
    		continue;
		}
		if(lx==1){
			if(find(x+n)==find(y)||find(x+2*n)==find(y)){
				ans++;
				continue;
			}
			update(x,y);update(x+n,y+n);update(x+2*n,y+2*n);
		}else{
			//x吃y
			if(find(x)==find(y)||find(x+2*n)==find(y)){
				ans++;
				continue;
			} 
			update(x,y+2*n);update(x+n,y);update(x+2*n,y+n);
		}
	}
	cout<<ans;
	return 0;
}

P9869

题目大意:n种元素间有相等,相反的关系,元素可以被复制为T,F,求一种方案使得未知赋值U数量最少

思路:分类并查集。

考虑两个类型

A:与它相等的元素

B:与它不等的元素

性质:如果元素i同时出现在A与B中,只能是U,即是有U=-U

val _{_{}^{i}}^{}表示元素i相关的元素编号,初始值val_{i}=i

1.直接赋值

被赋值为

F=-T

T=n+1

U=n+2

2.当val_{i }与元素i是相等关系时:相关元素val_{i}与当前元素相等,他们的相等集和不等集合并

   当val_{i}与元素i是不等关系时:相关元素val_{i}与当前元素不相等,他们当相等集与不等集交叉合并

if(val[i]>0){  //相等关系 
   merge(val[i],i);merge(val[i]+n,i+n);
}if(val[i]<0){ //不等关系
   merge(-val[i],i+n);merge(-val[i]+n,i);
}

3.查询

根据性质:

bool chk(int x,int y){
	return find(x)==find(y);
}
int ans=0;
	rep(i,1,n) 
	  if(chk(i,i+n))
	    ans++;
	cout<<ans<<endl;

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值