poj1733 带权并查集

时间:2023-03-09 00:15:53
poj1733 带权并查集

题意:有一个 0/1 数列,现在有n组询问和回答,表示某个区间内有奇数或者偶数个1,问到前多少个都没有逻辑错误,而下一个就不满足

可以定奇数为 1 偶数为 0作为每个元素的权值,表示它与它的祖先元素的差距,这样通过 mod2 可以直接表示两奇或两偶都得偶,奇偶得奇,然后就是对前序1的个数和做并查集的操作,注意对于一个区间[a,b],合并的两个元素是 a-1 和 b 。直到找到错误就可以了。由于区间太大,可以离散化各个需要合并的元素,再离线操作并查集。

 #include<stdio.h>
#include<string.h>
#include<map>
using namespace std; int fa[],n,m,num[],a[],b[],v[];
char s[]; void init(){
for(int i=;i<=;i++)fa[i]=i;
memset(num,,sizeof(num));
} int find(int x){
int r=x,c=,t1,t2;
while(r!=fa[r]){
c+=num[r];
r=fa[r];
}
while(x!=r){
t1=fa[x];
t2=c-num[x];
fa[x]=r;
num[x]=c%;
x=t1;
c=t2;
}
return r;
} int main(){
scanf("%d%d",&n,&m);
init();
int i,cnt=,ans=;
map<int,int>M;
for(i=;i<=m;i++){
scanf("%d%d%s",&a[i],&b[i],s);
a[i]--;
if(s[]=='e')v[i]=;
else v[i]=;
if(!M[a[i]])M[a[i]]=++cnt;
if(!M[b[i]])M[b[i]]=++cnt;
}
bool f=;
for(i=;i<=m;i++){
int x=find(M[a[i]]),y=find(M[b[i]]);
if(x!=y){
num[x]=((num[M[b[i]]]+v[i]-num[M[a[i]]])%+)%;
fa[x]=y;
if(f)ans++;
}
else{
if(f){
if(!v[i]&&num[M[a[i]]]==num[M[b[i]]])ans++;
else if(v[i]&&num[M[a[i]]]!=num[M[b[i]]])ans++;
else f=;
}
}
}/*
for(i=1;i<=m;i++){
printf("%d %d %d\n",M[a[i]],M[b[i]],v[i]);
}
for(i=1;i<=cnt;i++){
printf("%d %d %d\n",i,fa[i],num[i]);
}*/
printf("%d\n",ans);
return ;
}