写在前面:
对于带权并查集,我们最重要的一个函数就是d函数,d[x]代表到达x的父节点的距离。所以对于带权并查集更新的时候我们一般画图,分别画,a,b及他们的父亲节点的图,然后分fa,fb相同和不同的两种情况。一般我们是询问情况的话,有几种情况就是模几的情况下。分别(d[a]+d[b])%mod是否等于我们a与b的关系然后判断矛盾。mod2的情况的加减运算就是我们的亦或运算。
对于判断关系的并查集我们还可以用拓展域并查集来写,但是有时候关系太多会超时。
接下来以下面的题来写两种方法。
奇偶游戏
小A和小B在玩一个游戏。
首先,小A写了一个由0和1组成的序列S,长度为N。
然后,小B向小A提出了M个问题。
在每个问题中,小B指定两个数 l 和 r,小A回答 S[l~r] 中有奇数个1还是偶数个1。
机智的小B发现小A有可能在撒谎。
例如,小A曾经回答过 S[1~3] 中有奇数个1, S[4~6] 中有偶数个1,现在又回答 S[1~6] 中有偶数个1,显然这是自相矛盾的。
请你帮助小B检查这M个答案,并指出在至少多少个回答之后可以确定小A一定在撒谎。
即求出一个最小的k,使得01序列S满足第1k个回答,但不满足第1k+1个回答。
输入格式
第一行包含一个整数N,表示01序列长度。
第二行包含一个整数M,表示问题数量。
接下来M行,每行包含一组问答:两个整数l和r,以及回答“even”或“odd”,用以描述S[l~r] 中有奇数个1还是偶数个1。
输出格式
输出一个整数k,表示01序列满足第1k个回答,但不满足第1k+1个回答,如果01序列满足所有回答,则输出问题总数量。
数据范围
N≤109,M≤10000
输入样例:
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
输出样例:
3
带权并查集:
这里用了前缀和的思想。前缀和数组一定能构造出一个a仅存在01的数组。
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int s[N],pre[N],d[N];
int find(int x)
{
if(x!=pre[x]){
int rt=find(pre[x]);
d[x]^=d[pre[x]];
pre[x]=rt;
}
return pre[x];
}
//定义d函数d[x]代表x到达根节点的距离
int cnt;
unordered_map<int,int> ma;
int get_hash(int x)
{
if(!ma.count(x)) ma[x]=++cnt;
return ma[x];
}
int main()
{
int n,m; scanf("%d%d",&n,&m);
for(int i=1;i<=20010;i++) pre[i]=i;
int res=m;
for(int i=1;i<=m;i++){
int a,b;
string str;
cin>>a>>b>>str;
int t=0;
if(str=="odd") t=1;
a=get_hash(a-1);
b=get_hash(b);
int fx=find(a);
int fy=find(b);
if(fx!=fy){
pre[fx]=fy;
d[fx]=d[a]^d[b]^t;
}else{
if((d[a]^d[b])!=t){
res=i-1;
break;
}
}
}
cout<<res<<endl;
}
拓展域并查集:
拓展域合并的是一个大集合,集合中存在各种关系。只有有一个关系成立,那么这个集合中的所有关系成立,而不像我们的边权并查集是两两之间的关系。
#include <bits/stdc++.h>
using namespace std;
const int N=40020,base=N/2;
int s[N],pre[N],d[N];
int find(int x) {
if (x != pre[x]) return pre[x] = find(pre[x]);
return pre[x];
}
int cnt;
unordered_map<int,int> ma;
int get_hash(int x) {
if (!ma.count(x)) ma[x] = ++cnt;
return ma[x];
}
int main()
{
int n,m; scanf("%d%d",&n,&m);
for(int i=1;i<=40020;i++) pre[i]=i;
int res=m;
for(int i=1;i<=m;i++){
int a,b;
string str;
cin>>a>>b>>str;
a=get_hash(a-1);
b=get_hash(b);
if(str=="odd"){
if(find(a+base)==find(b+base)){
res=i-1;
break;
}
pre[find(a)]=find(b+base);
pre[find(a+base)]=find(b);
}else{
if(find(a)==find(b+base)){
res=i-1;
break;
}
pre[find(a)]=find(b);
pre[find(a+base)]=find(b+base);
}
}
cout<<res<<endl;
}