文章目录
蓝桥杯2018决赛 版本分支
【题目描述】
小明负责维护公司一个奇怪的项目。这个项目的代码一直在不断分支(branch)但是从未发生过合并(merge)。
现在这个项目的代码一共有N个版本,编号1~N,其中1号版本是最初的版本。
除了1号版本之外,其他版本的代码都恰好有一个直接的父版本;即这N个版本形成了一棵以1为根的树形结构。
如下图就是一个可能的版本树:
1
/ \
2 3
| / \
5 4 6
现在小明需要经常检查版本x是不是版本y的祖先版本。你能帮助小明吗?
【输入格式】
第一行包含两个整数N和Q,代表版本总数和查询总数。
以下N-1行,每行包含2个整数u和v,代表版本u是版本v的直接父版本。
再之后Q行,每行包含2个整数x和y,代表询问版本x是不是版本y的祖先版本。
对于30%的数据,1 <= N <= 1000 1 <= Q <= 1000
对于100%的数据,1 <= N <= 100000 1 <= Q <= 100000
【输出格式】
对于每个询问,输出YES或NO代表x是否是y的祖先。
【样例输入】
6 5
1 2
1 3
2 5
3 6
3 4
1 1
1 4
2 6
5 2
6 4
【样例输出】
YES
YES
NO
NO
NO
测试网址:http://oj.ecustacm.cn/problem.php?id=1400
个人解法
【解法一】
思路解析:使用了最近公共祖先LCA算法+倍增思想。(暂未做思路解析,有需要再补)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.LinkedList;
import java.util.Queue;
public class _版本分支 {
static int n, q, tot = 0, p = 17 + 1, N = (int) 1e5 + 5;
static int[][] parents;
static int[] head, depth;
static Node[] ee;
public static void main(String[] args) throws IOException {
InputReader sc = new InputReader(System.in);
n = sc.nextInt();
q = sc.nextInt();
parents = new int[N][p];
head = new int[N];
ee = new Node[N];
depth = new int[N];
for (int i = 1; i < n; ++i) {
int u = sc.nextInt(), v = sc.nextInt();
parents[v][0] = u;
add(u, v);
}
build(1);
while (q-- > 0) {
int x = sc.nextInt(), y = sc.nextInt();
int p = lca(x, y);
if (p == x)
System.out.println("YES");
else
System.out.println("NO");
}
}
public static int lca(int x, int y) {
if (depth[x] < depth[y]) {
int t = x;
x = y;
y = t;
}
for (int i = p - 1; i >= 0; --i) {
if (depth[parents[x][i]] >= depth[y])
x = parents[x][i];
}
if (x == y)
return x;
for (int i = p - 1; i >= 0; --i) {
if (parents[x][i] != parents[y][i]) {
x = parents[x][i];
y = parents[y][i];
}
}
return parents[x][0];
}
public static void build(int start) {
depth[start] = 1;
int[] que = new int[N];
int hh = 0, tt = 0;
que[hh] = start;
while (hh <= tt) {
int p = que[hh++];
for (int i = head[p]; i != 0; i = ee[i].next) {
int t = ee[i].to;
depth[t] = depth[p] + 1;
for (int k = 1; k < p; ++k)
parents[t][k] = parents[parents[t][k - 1]][k - 1];
que[++tt] = t;
}
}
}
public static void add(int from, int to) {
ee[++tot] = new Node(from, to, head[from]);
head[from] = tot;
}
static class Node {
int from, to, next;
public Node(int from, int to, int next) {
this.from = from;
this.to = to;
this.next = next;
}
}
static class InputReader {
StreamTokenizer tokenzier;
public InputReader(InputStream stream) {
tokenzier = new StreamTokenizer(new BufferedReader(new InputStreamReader(stream)));
tokenzier.ordinaryChars(33, 126);
tokenzier.wordChars(33, 126);
}
public String next() throws IOException {
tokenzier.nextToken();
return tokenzier.sval;
}
public int nextInt() throws IOException {
return Integer.parseInt(next());
}
}
}
- 附加拓展
public static void build(int start) {
depth[start] = 1;
Queue<Integer> que = new LinkedList<Integer>();
que.add(start);
while (!que.isEmpty()) {
int p = que.poll();
for (int i = head[p]; i != 0; i = ee[i].next) {
int t = ee[i].to;
depth[t] = depth[p] + 1;
for (int j = 1; j < p; ++j)
parents[t][j] = parents[parents[t][j - 1]][j - 1];
que.add(t);
}
}
}
这种写法对于java来说容易报运行错误,当版本1是其他所有版本的直接父版本时,队列就有可能一下子放入100000-1个元素,会导致爆栈。 对于不清楚入队次数时,可以使用队列;而清楚入队次数时,使用上面第一种更适合。 同理对于dfs版的build也是会存在报栈的可能。