推荐:http://www.cnblogs.com/JVxie/p/4854719.html
B. Borrow Classroom
Time Limit: 5000msMemory Limit: 262144KB 64-bit integer IO format: %lld Java class name: Main
Submit Status PID: 52509
每年的BNU校赛都会有两次赛前培训,为此就需要去借教室,由于SK同学忙于出题,这个事情就由小Q同学来跑腿。SK同学准备从宿舍出发,把借教室的单子交给小Q同学让他拿去教务处盖章,但是何老师突然发现SK同学好像借错教室了,想抢在借教室的单子被送到教务处之前拦截下来。
现在把校园抽象成一棵n个节点的树,每条边的长度都是一个单位长度,从1到n编号,其中教务处位于1号节点,接下来有q个询问,每次询问中SK同学会从B号节点出发,到C号节点找到小Q同学并将借教室的单子交给他,然后小Q同学再从C号节点出发前往教务处,何老师会从A号节点出发开始拦截。
所有人在一个单位时间内最多走一个单位距离,只要何老师在单子还没被送到教务处之前遇到拿着单子的同学都算拦截成功,如果小Q同学已经到了教务处,那么由于小Q同学手速极快,单子会被立即交上去,即使何老师到了教务处也无济于事,你需要判断何老师是否能够拦截成功。
Input
第一行是一个正整数T(leq 5),表示测试数据的组数,
对于每组测试数据,
第一行是两个整数n,q(1leq n,q leq 100000),分别表示节点数和询问数,
接下来n-1行,每行包含两个整数x,y(1leq x,y leq n),表示x和y之间有一条边相连,保证这些边能构成一棵树,
接下来q行,每行包含三个整数A,B,C(1 leq A,B,C leq n),表示一个询问,其中A是何老师所在位置,B是SK同学所在位置,C是小Q同学所在位置,保证小Q同学初始不在教务处。
Output
对于每个询问,输出一行,如果何老师能成功拦截则输出”YES”(不含引号),否则输出”NO”(不含引号)。
Sample Input
1
7 2
1 2
2 3
3 4
4 7
1 5
1 6
3 5 6
7 5 6
Sample Output
YES
NO
官方题解:以1为根,记A->1的路径长度为a,B->C->的路径长度为b,如果a小于b一定能拦截,如果a>b一定不能拦截,如果a=b,讨论一下何老师最早能遇到单子的位置,其实就是LCA(A,C),如果LCA不为1那么能拦截,否则不能拦截,只需要支持快速查询两点的LCA,也就能快速查询两点之间的距离
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <set>
#include <bitset>
#include <iomanip>
#include <list>
#include <complex>
#include <stack>
#include <utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
const double eps = 1e-8;
const int INF = 1e9+7;
const ll inf =(1LL<<62) ;
const int MOD = 1e9 + 7;
const ll mod = (1LL<<32);
const int N =1e6+6;
const int M=100010;
const int maxn=1e5+10;
struct Node
{
int to,next;
} edge[maxn*2];
int first[maxn],sz;
int top[maxn],father[maxn],son[maxn];
int num[maxn],deep[maxn];
void addedge(int s,int t)//链接表建图
{
edge[sz].to=t;
edge[sz].next=first[s];
first[s]=sz;
//printf("%d %d %d %d %d %d
",s,t,sz,edge[sz].to,edge[sz].next,first[s]);
sz++;
}
void dfs(int x,int pre,int Deep)//记录每个节点的深度,父节点,子节点,num和top不知道干啥的
{
//printf("x=%d pre=%d Deep=%d
",x,pre,Deep);
deep[x]=Deep;
father[x]=pre;
num[x]=1;
//printf("deep[%d]=%d father[%d]=%d num[%d]=%d
",x,deep[x],x,father[x],x,num[x]);
for(int i=first[x]; i!=-1; i=edge[i].next)
{
//printf("i=%d
",i);
int to=edge[i].to;
//printf("to=%d pre=%d
",to,pre);
if(to!=pre)
{
dfs(to,x,Deep+1);
num[x]+=num[to];
if(son[x]==-1||num[to]>num[son[x]])
son[x]=to;
//printf("num[%d]=%d son[%d]=%d
",x,num[x],x,son[x]);
}
}
}
void getlist(int x,int pre)//好像记录top,(不理解
{
//printf("x=%d pre=%d
",x,pre);
top[x]=pre;
//printf("top[%d]=%d
",x,top[x]);
//printf("son[%d]=%d
",x,son[x]);
if(son[x]!=-1)
getlist(son[x],pre);
else return ;
for(int i=first[x]; i!=-1; i=edge[i].next)
{
//printf("first[%d]=%d
",x,i);
int to=edge[i].to;
//printf("to=%d son[%d]=%d father[%d]=%d
",to,x,son[x],x,father[x]);
if(to!=son[x]&&to!=father[x])
getlist(to,to);
}
}
int lca(int x,int y)//返回什么重要要的值
{
//printf("x=%d y=%d
",x,y);
//printf("top[%d]=%d top[%d]=%d
",x,top[x],y,top[y]);
while(top[x]!=top[y])
{
//printf("top[x]=%d top[y]=%d
",)
//printf("deep[%d]=%d deep[%d]=%d
",top[x],deep[top[x]],top[y],deep[top[y]]);
if(deep[top[x]]<deep[top[y]])
swap(x,y);
//printf("father[%d]=%d
",top[x],father[top[x]]);
x=father[top[x]];
//printf("x=%d
",x);
//printf("top[%d]=%d top[%d]=%d
",x,top[x],y,top[y]);
}
//printf("deep[%d]=%d deep[%d]=%d
",x,deep[x],y,deep[y]);
if(deep[x]>deep[y])
swap(x,y);
return x;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,q;
scanf("%d%d",&n,&q);
sz=0;
memset(first,-1,sizeof(first));
memset(son,-1,sizeof(son));
for(int i=1; i<n; i++)
{
int s,t;
//s=read(),t=read();
scanf("%d%d",&s,&t);
addedge(s,t);
addedge(t,s);
}
dfs(1,0,1);
getlist(1,1);
while(q--)
{
int a,b,c,LCA;
//a=read(),b=read(),c=read();
scanf("%d%d%d",&a,&b,&c);
LCA=lca(b,c);
int s1=abs(deep[1]-deep[b])+2*abs(deep[LCA]-deep[c]);//计算b到c到1的距离
int s2=abs(deep[1]-deep[a]);//计算a到1的距离
if(s1<s2) puts("NO");
else if(s1>s2) puts("YES");
else
{
if(lca(c,a)==1) puts("NO");
else puts("YES");
}
}
}
return 0;
}