題目連接
題意:
一個N 個節點的樹 N-1條邊, 1號節點是根節點,Q次詢問,每次詢問3個整數
A B C 3個人(3個整數代表3個人所處的位置),
要求A 在文件交到根節點前進行攔截
如果A C同時到1則算失敗
數據範圍
3s
最多5組樣例
n q <= 1e5
時間分析可以用靜態的Tarjan來求LCA 和倍增 求LCA
離線算法 用Tarjan可能會快一點都試試吧
思路:
1) A到1的距離 要小於( B到 C的距離 + C到 1的距離)
2) 如果 A到1的距離 等於( B到 C的距離 + C到 1的距離)要判斷是否在中途相遇 即LCA(a,c) != 1
3)A到1 的距離是 A的樹深,
4)B到C的距離可以用LCA(a, b)來求,dis[a] + dis[b] - 2 * dis[LCA(a,b)]
5) C到1的距離是 C的樹深
LCA 倍增AC:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int head[MAXN], cnt;
int depth[MAXN], fa[MAXN][30];
struct Edge{
int to, dis, next;
}edge[MAXN << 1];
int n, q;
void add_edge(int u, int v, int dis) {
edge[++cnt].to = v;
edge[cnt].dis = dis;
edge[cnt].next = head[u];
head[u] = cnt;
}
void init () {
cnt = 1;
memset(head, 0, sizeof(head));
memset(depth, 0, sizeof(depth));
memset(fa, 0, sizeof(fa));
}
void DFS(int now, int pre) {
depth[now] = depth[pre] + 1;
fa[now][0] = pre;
for(int i = 1; (1 << i) <= depth[now]; ++i) {
fa[now][i] = fa[fa[now][i - 1]][i - 1];
}
for (int i = head[now]; i; i = edge[i].next) {
if(edge[i].to != pre)
DFS(edge[i].to, now);
}
}
int LCA(int a, int b) {
if(depth[a] < depth[b]) swap(a, b);
int dep = depth[a] - depth[b];
for (int i = 0; (1 << i) <= dep; ++i) {
if((1 << i) & dep) {
a = fa[a][i];
}
}
if(a == b) return a;
for (int i = log2(depth[a]); i >= 0; --i) {
//如果父親不同就向上跳, 如果父親相同就減小距離再比較,直到不相同在跳。
if (fa[a][i] != fa[b][i]) {
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];
}
int distant(int a, int b) {
int temp = LCA(a, b);
return depth[a] + depth[b] - 2 * depth[temp];
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
init();
cin >> n >> q;
int x, y;
for(int i = 1; i < n; ++i) {
scanf("%d%d", &x, &y);
add_edge(x, y, 0);
add_edge(y, x, 0);
}
int A, B, C;
DFS(1, 1);
for(int i = 1; i <= q; ++i) {
scanf("%d%d%d", &A, &B, &C);
int dis_BC = distant(B, C);
int dis_A1 = depth[A];
int dis_C1 = depth[C];
bool flag = 0;
if(dis_A1 < dis_BC + dis_C1) flag = 1;
else if ((dis_A1 == dis_BC + dis_C1) && LCA(A, C) != 1) flag = 1;
if(flag) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}