P2024
题目大意:给出x是y的同类,x吃y两种关系,求出他们的关系假话总数
思路:维护两个元素的关系较复杂,需要一个元素维护3个部分
A:该元素与它的同类
B:该元素与它的猎物
C:该元素与它的天敌
判断一句话是否为假:
1.xy是同类:是否有吃与被吃,天敌与是天敌的关系
即是和
添加关系:一对一添加
2.x吃y:是否有为同类,y吃x(y是x的天敌)的关系
添加关系:
y的同类作为x的猎物
y的猎物作为x的天敌
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,即是有
记表示元素i相关的元素编号,初始值
1.直接赋值
被赋值为
2.当与元素i是相等关系时:相关元素
与当前元素相等,他们的相等集和不等集合并
当与元素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;
B. 公司搬迁
题意:对于两个集合A,B,如果元素x在A中,那么a-x也在A中,B中同理
思路:
分两类:与x性质相同;与x性质相反,显然两者互斥
1.如果该元素只能与A关联,那么元素x与a-x相连,元素x+n与元素a-x+n相连
2.与B关联:同理
3.没有集合与之关联:一定非法
4.两者都关联:a!=b,显然,a-x与b-x一定不能在同一类之内。所以a-x与b-x+n相连,b-x与a-x+n相连。
5.在其中一个集合中,x=a(b)-x:保持原有性质,没有相连关系
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int T,n,na,nb,a[N],fa[N];
map<int,int> w;
int find(int x){
if(fa[x]!=x) return fa[x]=find(fa[x]);
else return x;
}
void add(int x,int y){
int f1=find(x),f2=find(y);
if(f1!=f2){
fa[f1]=f2;
}
}
void solve() {
w.clear();
cin>>n>>na>>nb;
for(int i=1;i<=n;i++){
cin>>a[i];w[a[i]]=i;
}
for(int i=1;i<=2*n;++i) fa[i]=i;
bool fs=true;
for(int i=1;i<=n;i++){
//A:x na-x B:y+n nb-y+n
int x=w[na-a[i]],y=w[nb-a[i]];
if(!x&&!y){
fs=false;
break;
}
if(!x){
fa[i]=fa[find(y)];
fa[i+n]=fa[find(y+n)];continue;
}
if(!y){
fa[i]=fa[find(x)];
fa[i+n]=fa[find(x+n)];
continue;
}
if(x==i||y==i) continue;
//if(x&&y){
fa[x]=fa[find(y+n)];
fa[y]=fa[find(x+n)];
// }
}
for(int i=1;i<=n;i++)
if(find(i)==find(i+n))
fs=false;
if(fs) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
signed main() {
cin >> T;
while (T--) solve();
return 0;
}
本文介绍了分类并查集在解决特定类型问题中的应用,包括通过维护不同类型的元素关系来判断真假命题,以及利用并查集减少未知赋值的数量等问题。通过具体的算法实现展示了如何高效地处理这些复杂的关系。
2882

被折叠的 条评论
为什么被折叠?



