记录编号 269515 评测结果 AAAAAAAAAA
题目名称 [NOIP 2013]货车运输 最终得分 100
用户昵称 Gravatar安呐一条小咸鱼。 是否通过 通过
代码语言 C++ 运行时间 0.069 s
提交时间 2016-06-13 17:45:09 内存使用 2.96 MiB
显示代码纯文本
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=10010;
int n,m,r1,r2,ask,x,y,t;
int father[maxn],Deep[maxn];
//并查集判断是否连通 用来输出-1; 
int mind[maxn][20],f[maxn][20];
//类似dp的 统计x与y之间路径的最小值 ; 
/*
开一个数组f[i][j],来表示i 点的2j 祖先那么f[i][0] 就
表示i 的父亲节点。
f[i][j] = f[ f[i][j-1] ][j-1]
i 点的2j .. 1 祖先的2j .. 1 祖先就是i 点的2j 祖先。
*/ 
int head[maxn<<2],tot=0;
inline int Read() 
{
	int ret;
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	ret=ch-'0';
	ch=getchar();
	while(ch>='0'&&ch<='9')
	{
		ret=ret*10+ch-'0';
		ch=getchar();
	}
	return ret;
}
int find(int x)
{
	if(father[x]!=x)
	{
		father[x]=find(father[x]);
	}
	return father[x];
}
inline bool unionn(int x,int y)
{
	r1=find(x);
	r2=find(y);
	if(r1==r2)return false;//已经合并 false
	else 
	{
		father[r2]=r1;
		return true;
	}
}
class Node
{
	public:
	    int to,next,v;
}edge[maxn<<2];
struct tree
{
	int s,t,v;
}Tree[maxn<<2];
inline bool comp(const tree &a,const tree &b)
{
	return a.v>=b.v;
}
void Addedge(int x,int y,int v)
{
	edge[++tot].to=y;
	edge[tot].v=v;
	edge[tot].next=head[x];
	head[x]=tot;
}
void Build(int x,int deep)
{
	Deep[x]=deep;//更新deep 
	for(int i=head[x];i!=0;i=edge[i].next)
	{
		int cy=edge[i].to; 
		if(!f[cy][0])//
		{
			f[cy][0]=x;
			mind[cy][0]=edge[i].v;
			Build(cy,deep+1);
		}
	}
}
inline int Lca(int x,int y)
/*
要找到距离i 节点最远的祖先:for (int j = 20;j >= 0;j --)
if (f[i][j]) i = f[i][j] ;
		__O(log2n)__
上翻法把x 调到与y 相同的深度,此时
若x == y 那么此时的x 或y 就是他们的LCA
否则,接下来同时把x 和y 向上翻,直到f[x][0] == f[y][0], 此
时的fa[x][0] 就是LCA 。
*/ 
{
	int ret=0x7fffffff;
	if(Deep[x]<Deep[y])swap(x,y);
	for(int i=t;i>=0;i--)
	{
		if( Deep[ f[x][i] ]>=Deep[y])
		{
			ret=min(ret,mind[x][i]);
			x=f[x][i];
		}
	}
	if(x!=y)
	{
		for(int i=t;i>=0;i--)
		{
			if(f[x][i]!=f[y][i])
			{
				ret=min( ret ,  min( mind[x][i],mind[y][i] ) );
				x=f[x][i];
				y=f[y][i];
			}
		}
		ret=min( ret ,  min( mind[x][0],mind[y][0] ) );
	}
	return ret;
}
int main()
{
	freopen("truck.in","r",stdin);
	freopen("truck.out","w",stdout);
	n=Read();
	m=Read();
	for(int i=1;i<=n;i++)
	{
		father[i]=i;
	}
	for(int i=1;i<=m;i++)
	{
		Tree[i].s=Read();
		Tree[i].t=Read();
		Tree[i].v=Read();
	}
	sort(Tree+1,Tree+m+1,comp);//按载重排序; 
	for(int i=1;i<=m;i++)
	{
		if(unionn(Tree[i].s,Tree[i].t))//没有连接;加边; 
		{
			Addedge(Tree[i].s,Tree[i].t,Tree[i].v);
			Addedge(Tree[i].t,Tree[i].s,Tree[i].v);
		}
	}
	t=log(  (2*n-1)*1.0  )/log(2.0);
	for(int i=1;i<=t;i++)//初始化; 
	{
		if(!f[i][0])
		{
			f[i][0]=i;
			mind[i][0]=0x7fffffff;
			Build(i,1);//从deep=1开始向下搜。 
		}
	}
	for(int j=1;j<=t;j++)
	{
		for(int i=1;i<=n;i++)
		{
			f[i][j]=f[ f[i][j-1] ][j-1];//i点的2^j-1祖先的2^j-1祖先就是i点的2^j祖先
			mind[i][j]=min(mind[i][j-1],mind[ f[i][j-1] ][j-1]);
			/*
			对于此类问题,由于我们只关注最大(最小)的那条边,那么我们可以先sort一遍,然后像Kruskar一样加边,
			此时我们查询x,y之间的最值只需要统计树上x与y之间路径的最小值即可,用倍增LCA
			*/
		} 
	}
	ask=Read();
	for(int i=1;i<=ask;i++)
	{
		x=Read();
		y=Read();
		if(find(x)!=find(y))puts("-1");//没有连接 直接输出-1;
		else
		{
			printf("%d\n",Lca(x,y));
		} 
	}
	return 0;
}