先安利一波好oj->这里
【问题描述】
老爸出差了,你和你的妈妈准备去走人家,但是你的亲戚们数量太多了,关系网过於庞大,要判断两个是否是亲戚,确实还很不容易,现在给出一些消息,你想知道某两人是否是亲戚关系
规定:如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚,
如:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。
但是由于亲戚们的感情并不是总是那么好的,所以随时会出现反目的现象。倘若某对亲戚反目了,x的亲戚和y的亲戚也就不再是亲戚。
如:x,y反目了,y,z是亲戚,x,z就不是亲戚。
由于亲戚们的性格不是那么好(有点2),所以他们在反目之后就不会再和好。他们各自的亲戚们也不会再成亲戚。
如:x和y反目了,x的亲戚就不会和y的亲戚再成亲戚关系。
在这里,只有直接的亲戚才会反目(废话,不直接的亲戚他们认都不认识,怎么会反目?!)同时我们保证不存在有一个人两难的情况
两难:假设z既是x的直接亲戚,又是y的直接亲戚,那x和y反目后,z就会出现两难的情况。
另外,我们规定自己是自己的亲戚。
第一行包含两个正整数n、m、q分别表示有n个人,m个消息,q对亲戚
以下m行:每行两个数Mi,Mj(1<=Mi,Mj<=N),表示Mi和Mj是亲戚。
接着q行,每行一个字符串C和两个数Mi,Mj(1<=Mi,Mj<=N)
如果C=’A’,表示询问Mi和Mj是否为亲戚。
如果C=’F’,表示Mi和Mj反目了(保证Mi和Mj是直接亲戚关系)
若干行,对于每个询问输出’Yes’表示他们是亲戚,或’No’表示他们不是亲戚
很裸的一道离线处理并查集,先读入开始的所有边,然后将每条边的小端点作为map的第一位,大端点作为第二位,存下当前边的编号,再读入所有操作,记录需要删的边,用map确定编号,让vis当前编号=1,然后下面开始构图,如果当前边的vis不是1,那么合并两个端点集合,否则continue,然后将所有操作逆序进行,读到删边操作,用map确定要删哪条边,然后把这条边加进来,读到判断操作,就将判断结果加入ans就好了
代码
#include<map> #include<cmath> #include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int M=40000; map<int,int>jud[M];//确定编号 struct edge { int a,b; }emm[M];//存初始边 struct que { int a,b; char s; }gll[M];//存操作 int fa[M],si[M],ans[M]; int n,m,q,cnt,vis[M]; int find(int x) { if (fa[x]!=x) return fa[x]=find(fa[x]); return x; }//路径压缩 void unionn(int a,int b) { if (si[a]<=si[b]) fa[a]=b,si[b]+=si[a]; else fa[b]=a,si[a]+=si[b]; return ; }//按秩合并 void constt() { for (int i=1;i<=n;i++) fa[i]=i,si[i]=1; return ; }//初始化 int main() { scanf("%d%d%d",&n,&m,&q);constt(); for (int i=1;i<=m;i++) { int x,y;scanf("%d%d",&x,&y); emm[i].a=min(x,y);emm[i].b=max(x,y); jud[emm[i].a][emm[i].b]=i;//存入编号 } for (int i=1;i<=q;i++) { char fu;int x,y; scanf("%s %d %d",&fu,&x,&y); gll[i].s=fu;gll[i].a=min(x,y);gll[i].b=max(x,y); if (fu=='F') vis[jud[gll[i].a][gll[i].b]]=1; }//确定哪条边不加入 for (int i=1;i<=m;i++) { if (vis[jud[emm[i].a][emm[i].b]]) continue; int r1=find(emm[i].a); int r2=find(emm[i].b); if(r1!=r2) unionn(r1,r2); }//构图 for (int i=q;i>0;i--) { int r1=find(gll[i].a); int r2=find(gll[i].b); if (gll[i].s=='F') unionn(r1,r2); else if (r1==r2) ans[i]=1; else if (r1!=r2) ans[i]=2; }//执行操作 for (int i=1;i<=q;i++) if (ans[i]==1) puts("Yes"); else if (ans[i]==2) puts("No"); return 0; }