并查集-进阶题目
一、食物链
题目描述
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。Output
只有一个整数,表示假话的数目。
Sample
Inputcopy Outputcopy 100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5
思路分析
此题是一道带权并并查集的题目,根据题意描述,此题有三类动物A,B,C。A吃B,B吃C,C吃A。构成一个循环,dis:0,1,2分别表示三种类别,通过模运算构成循环。通过(dis[X]-dis[Y]+3)%3来判断X和Y是否属于同类别,为0则是同类别,反之则不是。
详细代码
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=50005;
int n,k;
int fa[maxn],dis[maxn];
void init(){
for(int i=1;i<=n;i++){
fa[i]=i,dis[i]=0;
}
}
int find(int x){
if(x==fa[x]) return x;
int root=find(fa[x]);
dis[x]+=dis[fa[x]],dis[x]%=3;
return fa[x]=root;
}
void unionn(int x,int y,int r){
int rx=find(x);
int ry=find(y);
if(rx!=ry){
fa[rx]=ry;
dis[rx]=(r+dis[y]-dis[x]+3)%3;
}
}
int main(){
scanf("%d%d",&n,&k);
init();
int ans=0;
for(int i=1;i<=k;i++){
int D,X,Y;
scanf("%d%d%d",&D,&X,&Y);
if(X>n || Y>n){
ans++;
continue;
}
if(D==2 && X==Y){
ans++;
continue;
}
int rx=find(X),ry=find(Y);
if(rx!=ry){
if(D==1){
unionn(X,Y,0);
}else{
unionn(X,Y,1);
}
}else{
int flag=(dis[X]-dis[Y]+3)%3;
if(D==1 && flag) ans++;
if(D==2 && flag!=1) ans++;
}
}
printf("%d\n",ans);
return 0;
}
二、 A Bug’s Life
题目描述
Description
协会的同学有两个爱好,一类非常爱模拟题目,尤其喜欢做输出英文的模拟题,另一类很喜欢优化时长,每次遇到TLE都会变得异常兴奋,特别喜欢卡时间的毒瘤数据。
phz假定每个人都只与另一类人合作,这样就可以在写模拟的时候TLE了!但是你想看看到底是不是这样。
Problem
给出一个协会内合作的列表,请你判断是否有同种爱好的同学一起合作。Input
输入的第一行包含数据的组数t,每组数据在第一行先给出人n的数量(至少一个,最多2000个),再给出实验数据的个数m(最多1000000个)用一个空格隔开。在下面m行中,每个实验数据以两个不同的人的编号给出,编号之间用一个空格来隔开
Output
每个输出包含"Scenario #i:",i为当前数据组数,如果phz的假设成立,另起一行输出“No suspicious bugs found!”,否则输出“Suspicious bugs found!”
Sample Input
2
3 3
1 2
2 3
1 3
4 2
1 2
3 4Sample Output
Scenario #1:
Suspicious bugs found!Scenario #2:
No suspicious bugs found!
思路分析
根据题意两种类别,通过%2类处理,结果通过(dis[x]-dis[y]+2)%2来判断,若为0则表示是同种类别同学一起合作,反之则不是。
详细代码
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=2002;
int t,n,m;
int cnt;
int pre[maxn],dis[maxn];
void init(){
for(int i=1;i<=n;i++){
pre[i]=i,dis[i]=0;
}
}
int find(int x){
if(x==pre[x]) return x;
int root=find(pre[x]);
dis[x]+=dis[pre[x]],dis[x]%=2;
return pre[x]=root;
}
void unionn(int x,int y){
int rx=find(x);
int ry=find(y);
if(rx!=ry){
pre[rx]=ry;
dis[rx]=(dis[y]+1-dis[x])%2;
}
}
int main(){
scanf("%d",&t);
while(t--){
printf("Scenario #%d:\n",++cnt);
scanf("%d%d",&n,&m);
bool flag=false;
init();
for(int i=0;i<m;i++){
int x,y;
scanf("%d%d",&x,&y);
int rx=find(x),ry=find(y);
if(rx!=ry){
unionn(x,y);
}else{
int fact=(dis[x]-dis[y]+2)%2;
if(fact==0){
flag=true;
}
}
}
if(flag) printf("Suspicious bugs found!\n");
else printf("No suspicious bugs found!\n");
printf("\n");
}
return 0;
}
三、 Find them, Catch them
题目描述
小明喜欢吃小熊软糖和小熊硬糖,现在小明收集了N(N<=100000)个这两种糖,编号为1~N。有M(M<=100000)个操作:
\1. D a b 表示 a 和 b 是属于不同种类的糖
\2. A a b 询问这两种糖是什么关系,如果他们是同种糖,那么输出“In the same gang.”。如果不是同种类的糖,那么输出 “In different gangs.”。 如果不确定输出 “Not sure yet.”Input
多组数据。第一行是数据总数 T (1 <= T <= 20)每组数据第一行是N、M,接下来M行是操作
Output
对于每一个A操作,回答"In the same gang.“或"In different gangs.” 或"Not sure yet."
Sample Input
1 5 5 A 1 2 D 1 2 A 1 2 D 2 4 A 1 4
Sample Output
Not sure yet. In different gangs. In the same gang.
思路分析
根据题意,两种糖,两个类别,同上题一样进行处理。注意:若ch=‘A’,rx!=ry,则不能确定。
详细代码
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=1e5+2;
int t,n,m;
int pre[maxn],dis[maxn];
void init(){
for(int i=1;i<=n;i++){
pre[i]=i,dis[i]=0;
}
}
int find(int x){
if(x==pre[x]) return x;
int root=find(pre[x]);
dis[x]+=dis[pre[x]],dis[x]%=2;
return pre[x]=root;
}
void unionn(int x,int y){
int rx=find(x);
int ry=find(y);
if(rx!=ry){
pre[rx]=ry;
dis[rx]=dis[y]+1-dis[x],dis[rx]%=2;
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
init();
char ch;
int x,y;
while(m--){
getchar();
scanf("%c%d%d",&ch,&x,&y);
int rx=find(x),ry=find(y);
if(ch=='A'){
if(rx!=ry){
cout<<"Not sure yet."<<endl;
}else{
int fact=(dis[x]-dis[y]+2)%2;
if(fact==1) cout<<"In different gangs."<<endl;
else cout<<"In the same gang."<<endl;
}
}else{ // 'D'
if(rx!=ry){
unionn(x,y);
}
}
}
}
return 0;
}
A'){
if(rx!=ry){
cout<<"Not sure yet."<<endl;
}else{
int fact=(dis[x]-dis[y]+2)%2;
if(fact==1) cout<<"In different gangs."<<endl;
else cout<<"In the same gang."<<endl;
}
}else{ // 'D'
if(rx!=ry){
unionn(x,y);
}
}
}
}
return 0;
}