Description
Input
Output
Sample Input
3 4
2 1 E 1 2 N
2 1 N 1 1 N
3 1 N 2 1 N
2 2 N 1 1 N
Sample Output
YES
YES
NO
NO
赛时
暴力40分
正解
并查集,首先提一下这个题是强制在线啊,给你的两个路有一个是没有用的,看起来很绕,其实就是根据上一次的答案来确定这一次的查询。
然后呢我们先画个图,跟上面不一样,我们将点看成一个矩阵:
我们以样例的第一个查询为例,是2 1 E,也就是2,1和3,1这两个方块(红),之后题目说我们把这条边堵上,之后问是否联通。那联系到我们上面的图就是这两个方块之间的边被打破了。也就是黄色节点之间的边。之后根据上面的图,发现节点一共是(n+1)*(n+1)个。
之后我们就考虑算法了,首先对于一个进来的a1,b1,c1,我们可以知道它与它要去的另个方块,中间的边,的两边节点是多少,之后我们查询这两个节点是否在同一个集合,如果是也输出no,不是就是输出yes,之后如果不是一个集合就要记得合并。然后就这么做就行了。
最后是初始化问题了,当一个节点在边上的时候,它的父亲就是1,否则就是自己,还有不要傻乎乎的搞二维并查集,对于每个点i,j搞个编号就行了。之后,对于我们的图是按平面直角坐标系做的,而i,j两重循环去转移的话,i是枚举行,j是枚举列,所以也就是j是横坐标,i是纵坐标,这里不要搞反了。
#include<cstdio>
#define N 707
using namespace std;
int n,k,f[N*N],g[N][N],cnt;
bool bz;
int get(int x){return x==f[x]?x:f[x]=get(f[x]);}
bool find(int x,int y){
int fx=get(x),fy=get(y);
if(fx==fy) return 1;
f[fx]=fy;//不同就合并
return 0;
}
int main(){
freopen("sox.in","r",stdin);
freopen("sox.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n+1;i++)
for(int j=1;j<=n+1;j++){//初始化节点
g[j][i]=++cnt;//记得是j在前i在后
if(i==1||j==1||i==n+1||j==n+1) f[g[j][i]]=1;//在便便就是1
else f[g[j][i]]=cnt;//自己
}
bz=1;//用来判断是第几个边被查询,1时就是第一条边,0时就是第二条边
for(int i=1;i<=k;i++){
int a1,b1,a2,b2,x,y;
char c1,c2;
scanf("%d %d %c %d %d %c",&a1,&b1,&c1,&a2,&b2,&c2);
if(!bz) a1=a2,b1=b2,c1=c2;//当bz=0时,查询第二条边,那么就赋一下值给a1,b1,c1 这样方便
if(c1=='E')
x=g[a1+1][b1],y=g[a1+1][b1+1];//边的两边的节点
else
x=g[a1][b1+1],y=g[a1+1][b1+1];//同理
bool b=find(x,y);//判断
if(b)
bz=0,printf("NO\n");
else
bz=1,printf("YES\n");
}
}