先安利一波好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;
}