现在的位置: 首页 > 综合 > 正文

【VIJOS1321】魔塔 并查集+建树+栈 ※※※※※有详细题解和代码注释

2016年09月23日 ⁄ 综合 ⁄ 共 2049字 ⁄ 字号 评论关闭

 VIJOS-P1321 魔塔

Time Limit: 1 Sec  Memory Limit: 128 MB

Description

    在魔塔中有N个房间和M条道路,每条道路上有一个怪,它可以被一种特殊的武器消灭,而每个房间中也存在一种武器。现在知道第I个房间中的武器编号为I,小明(主人翁)初始在J房间,小明想知道哪些房间是他可以去的。

Input

    第一行是N,J,M     接下来M行每行三个数Ai,Bi,Ci,分别代表Ai房间和Bi房间之间有路,且此处的怪物可以被Ci号武器消灭。

Output

    N行,如果I个房间可以到达,则在第I行输出Yes,否则输出No

Sample Input

6 4 6
1 2 1
1 3 2
2 4 4
3 4 4
3 5 3
5 6 6

Sample Output

1:Yes
2:Yes
3:Yes
4:Yes
5:Yes
6:No

HINT

数据范围  1< =m< =50000,1< =a,b,J< =n< =50000 提示:m,n< 50000不等于说数组可以只开到50000;输出前面无空格

Source

中文题我为什么要告诉你题意?好的,题意就是这样。

现在我要说题解!

首先wyf写了一个暴力,90分,我贴一下他的博客,说不定他写了呢~http://blog.csdn.net/wyfcyx_forever

然后我要说我的正解了。


思想:

      每次将能走的点跑一遍确定能开的边,这样就拓展了能走的点,重复操作以得出答案。

实现:

      就是先把每个节点用链表挂上其能开的边,然后维护并查集和树构还有一个栈。

并查集:一个点的父亲节点,用于判断连通(/与起点连通)

树构:一棵有向树,每次搜索时从树根扫,然后就可以O(节点数)扫过全部点,把能新开的边计入。

栈:每次当有新开的边将某并查集与起点连通时,把此并查集(树构)的根节点入栈。


具体实现可以参考代码,有新鲜出炉的注释。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 50500
#define inf 0x3f3f3f3f
using namespace std;
struct KSD
{
	int u,v,next;
}e[N],ke[N<<1];
int head[N],key[N],cnt,cdk;
void add(int u,int v)
{/*树构*/
	cnt++;
	e[cnt].v=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
void open(int u,int v,int ky)
{/*给每个点挂上能开的边*/
	cdk++;
	ke[cdk].u=u;
	ke[cdk].v=v;
	ke[cdk].next=key[ky];
	key[ky]=cdk;
}
int f[N];/*并查集*/
int find(int x)
{
	if(x==f[x])return x;
	return f[x]=find(f[x]);
}
int n,m,s;
int stk[N],top;/*栈*/
bool visit[N];
void dfs(int x)
{
	visit[x]=1;
	int i,u,v;
	int fa,fb;
	for(i=key[x];i;i=ke[i].next)
	{/*把x节点能开的边(门)都打开*/
		fa=find(ke[i].u);
		fb=find(ke[i].v);
		if(fa==fb)continue;/*额,你懂得*/
		if(fa==s)stk[++top]=fb,f[fb]=s;
		else if(fb==s)stk[++top]=fa,f[fa]=s;
		/*
			某节点被连到了起点,表示能走,
			那么就需要处理这个并查集中所有的节点,
			把它们能开的边(门)都打开,
			这时我们就需要
			1. 一个树构来实现高速遍历
			2. 一个链表来实现查找边。
		*/
		else
		{/*额,没关系的就自己弄个树构先存着吧*/
			add(fa,fb);
			f[fb]=fa;
		}
	}
	if(x!=s)for(i=head[x];i;i=e[i].next)
	{/*遍历树构中其它点*/
		v=e[i].v;
		dfs(v);
	}
}
int main()
{
//	freopen("test.in","r",stdin);
	int i,j,k;
	int a,b,c;
	int u,v,t;
	scanf("%d%d%d",&n,&s,&m);
	for(i=1;i<=n;i++)f[i]=i;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		open(a,b,c);
	}
	stk[++top]=s;
	while(top){dfs(stk[top--]);}
	/*
		栈中是从起点这个树根能遍历到的一些枝杈,
		它们本身也是一颗颗树
		而遍历过程中会有新的边变成可行,
		这样就会不停地加进来新的点/点集,
		或者说是树。
		而!top时遍历也就停止了,
		额,没看懂的话不妨手调。
	*/
	for(i=1;i<=n;i++)
	{
		printf("%d:",i);
		if(visit[i])puts("Yes");
		else puts("No");
	}
	return 0;
}




抱歉!评论已关闭.