链接:https://ac.nowcoder.com/acm/contest/1031/C
输入
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
输出
3
一、带权并查集
因为本题传递多个关系,x==y y==z x==z,x!=y z!=y x==z ,x==y z!=y x!=z
;
我们可以用异或和来表示,d【x】^d[y]=1表示不同,d[x] ^d[y]=0表示相同
#include <stdio.h>
#include <algorithm>
using namespace std;
struct node
{
int l,r,ans;
}query[5005];
int d[10005],fa[10005],a[10005];
int n,m,tot;
int find(int x)
{
if(x==fa[x])return x;
int root=find(fa[x]);d[x]^=d[fa[x]]; //不能用之前的模板,因为d【x】要从下往上逐步更新
return fa[x]=root;
}
void ish() //离散化
{
char str[5];
for(int i=1;i<=m;i++)
{
scanf("%d%d%s",&query[i].l,&query[i].r,str);
query[i].ans=(str[0]=='o'?1:0);
a[++tot]=query[i].l-1,a[++tot]=query[i].r;
}
sort(a+1,a+1+tot);
n=unique(a+1,a+1+tot)-a-1;
}
int q(int x) //查找离散化后元素的位置,也就是值
{
return lower_bound(a+1,a+1+n,x)-a;
}
int main()
{
scanf("%d%d",&n,&m);
ish();
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
int x=q(query[i].l-1),y=q(query[i].r);
int p=find(x),q=find(y);
if(p!=q)fa[p]=q,d[p]=d[x]^d[y]^query[i].ans; //如果不在同一个集合,路径x~y
else //ans=d[x] ^d[y] ^d[p] ,所有d[p]=d[x]^d[y]^ans
{
if(d[x]^d[y]!=query[i].ans) //在同一个集合
{
printf("%d\n",i-1);
return 0;
}
}
}
printf("%d",m);
}
拓展域
将x分成 x_odd x_even
y分成 y_odd y_even
如果相等 x_odd y_odd 合并 y_even x_even 合并
不相等 x_odd y_even 合并 x_even y_odd 合并
这样关系就被维护了
#include <stdio.h>
#include <algorithm>
using namespace std;
struct node
{
int l,r,ans;
}query[5005];
int fa[20005],a[10005];
int n,m,tot;
int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void ish()
{
char str[5];
for(int i=1;i<=m;i++)
{
scanf("%d%d%s",&query[i].l,&query[i].r,&str);
query[i].ans=str[0]=='o'?1:0;
a[++tot]=query[i].l-1,a[++tot]=query[i].r;
}
sort(a+1,a+1+tot);
n=unique(a+1,a+1+tot)-a-1;
}
int q(int x)
{
return lower_bound(a+1,a+1+n,x)-a;
}
int main()
{
scanf("%d%d",&n,&m);
ish();
for(int i=1;i<=2*n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
int x_odd=q(query[i].l-1),y_odd=q(query[i].r);
int x_even=x_odd+n,y_even=y_odd+n;
if(query[i].ans) //相同
{
if(find(x_odd)==find(y_even)) //矛盾
{
printf("%d",i-1);
return 0;
}
fa[find(x_odd)]=find(y_odd);
fa[find(x_even)]=find(y_even);
}
else //不同
{
if(find(x_odd)==find(y_odd)) //矛盾
{
printf("%d",i-1);
return 0;
}
fa[find(x_odd)]=find(y_even);
fa[find(x_even)]=find(y_odd);
}
}
printf("%d",m);
}
https://codeforces.com/contest/1263/problem/D
题意
two passwords a and b are equivalent if there is a letter, that exists in both a and b;
two passwords a and b are equivalent if there is a password c from the list, which is equivalent to both a and b.
即如果 有一个字母和别人相等 这两个属于同一个集合abcd……xyz包括所有集合
/*
这题难点就是如何将a b ab他们属于同一个集合 ,且计数为1
*/
#include <stdio.h>
int fa[28],a[28],n,cnt;
char s[55];
int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=26;i++)fa[i]=i;
while(n--)
{
scanf("%s",s);
for(int i=0;s[i];i++)a[s[i]-'a'+1]=1; //出现字母,即不合并有多少集合
for(int i=1;s[i];i++) //cnt =合并的次数
{
int p=find(s[i]-'a'+1),q=find(s[i-1]-'a'+1);
if(p!=q)fa[p]=q,cnt++; //出现新的字母,并放入同一个集合
} //某个字符串如果与之前的都不交叉,则
} //独立出来
for(int i=1;i<=26;i++)cnt-=a[i]; //独立出来的个数 =不合并-合并的次数
printf("%d",-cnt);
}