最近共同祖先
例题:祖孙询问
【题目描述】
已知一棵 n个节点的有根树。有 m 个询问,每个询问给出了一对节点的编号 x 和 y,询问 x 与 y的祖孙关系。
【输入】
输入第一行包括一个整数 n表示节点个数;
接下来 n行每行一对整数对 a 和 b 表示 a 和 b 之间有连边。如果 b 是 −1,那么 a就是树的根;
第 n+2行是一个整数 m表示询问个数;
接下来 m行,每行两个正整数 x 和 y,表示一个询问。
【输出】
对于每一个询问,若 x是 y 的祖先则输出 1,若 y 是 x 的祖先则输出 2,否则输出 0。
【输入样例】
10
234 -1
12 234
13 234
14 234
15 234
16 234
17 234
18 234
19 234
233 19
5
234 233
233 12
233 13
233 15
233 19
【输出样例】
1
0
0
0
2
【提示】
数据范围与提示:
对于 30% 的数据,1≤n,m≤103;
对于 100% 的数据,1≤n,m≤4×104,每个节点的编号都不超过 4×104 。
————————————————————————————————————————————————————
用的(2)
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int INF = Integer.MAX_VALUE >> 1;
static int N = 40010;
static int M = N * 2;
static int n;
static int m;
static int[] h = new int[N];
static int[] e = new int[M];
static int[] ne = new int[M];
static int idx;
static int[] q = new int[N];
static int[] depth = new int[N];
static int[][] fa = new int[N][16];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
int root = 0;
Arrays.fill(h, -1);
for (int i = 0; i < n; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
if (b == -1) {
root = a;
} else {
add(a, b);
add(b, a);
}
}
bfs(root);
m = sc.nextInt();
while (m-- > 0) {
int a = sc.nextInt();
int b = sc.nextInt();
int p = lca(a, b);
if (p == a) {
System.out.println(1);
} else if (p == b) {
System.out.println(2);
} else {
System.out.println(0);
}
}
}
private static int lca(int a, int b) {
if (depth[a] < depth[b]) {
int t = a;
a = b;
b = t;
}
for (int k = 15; k >= 0; k--) {
if (depth[fa[a][k]] >= depth[b]) {
a = fa[a][k];
}
}
if (a == b) return a;
for (int k = 15; k >= 0; k--) {
if (fa[a][k] != fa[b][k]) {
a = fa[a][k];
b = fa[b][k];
}
}
return fa[a][0];
}
private static void bfs(int root) {
Arrays.fill(depth, INF);
depth[0] = 0;
depth[root] = 1;
int hh = 0, tt = 0;
q[0] = root;
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (depth[j] > depth[t] + 1) {
depth[j] = depth[t] + 1;
q[++tt] = j;
fa[j][0] = t;
for (int k = 1; k <= 15; k++) {
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
}
private static void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
}
例题:距离
输入样例1:
2 2
1 2 100
1 2
2 1
输出样例1:
100
100
输入样例2:
3 2
1 2 10
3 1 15
1 2
3 2
输出样例2:
10
25
——————————————————————————————————————————————————
用的(3)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int N = 20010;
static int M = N * 2;
static int n;
static int m;
static int[] h = new int[N];
static int[] w = new int[M];
static int[] e = new int[M];
static int[] ne = new int[M];
static int idx;
static int[] p = new int[N];
static int[] dist = new int[N];
static int[] st = new int[N];
static int[] res = new int[N];//存储每个询问的结果
static ArrayList<Pair>[] query = new ArrayList[N];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
for (int i = 0; i < query.length; i++) {
query[i] = new ArrayList<>();
}
n = sc.nextInt();
m = sc.nextInt();
Arrays.fill(h, -1);
for (int i = 0; i < n - 1; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
int c = sc.nextInt();
add(a, b, c);
add(b, a, c);
}
for (int i = 0; i < m; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
if (a != b) {
query[a].add(new Pair(b, i));
query[b].add(new Pair(a, i));
}
}
for (int i = 1; i <= n; i++) {
p[i] = i;
}
dfs(1, -1);
tarjan(1);
for (int i = 0; i < m; i++) {
System.out.println(res[i]);
}
}
private static void dfs(int u, int fa) {
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
dist[j] = dist[u] + w[i];
dfs(j, u);
}
}
private static void tarjan(int u) {
st[u] = 1;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (st[j] == 0) {
tarjan(j);
p[j] = u;
}
}
for (Pair item : query[u]) {
int y = item.x;
int id = item.y;
if (st[y] == 2) {
int anc = find(y);
res[id] = dist[u] + dist[y] - dist[anc] * 2;
}
}
st[u] = 2;
}
private static int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
private static void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
}
class Pair{
int x;//存查询的另一个点
int y;//存查询编号
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
}
356. 次小生成树
【TLE】了,这题Java估计是过不了了…
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
public class Main {
static int INF = Integer.MAX_VALUE >> 1;
static int N = 100010;
static int M = 300010;
static int n;
static int m;
static int[] h = new int[N];
static int[] w = new int[M];
static int[] e = new int[M];
static int[] ne = new int[M];
static int idx;
static Edge[] edge = new Edge[M];
static int[] p = new int[N];
static int[] depth = new int[N];
static int[][] fa = new int[N][17];
static int[][] d1 = new int[N][17];
static int[][] d2 = new int[N][17];
static int[] q = new int[N];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] s1 = br.readLine().split(" ");
n = Integer.parseInt(s1[0]);
m = Integer.parseInt(s1[1]);
for (int i = 0; i < m; i++) {
String[] s2 = br.readLine().split(" ");
int a = Integer.parseInt(s2[0]);
int b = Integer.parseInt(s2[1]);
int c = Integer.parseInt(s2[2]);
edge[i] = new Edge(a, b, c);
}
long sum = kruskal();
build();
bfs();
long res = (long) 1e18;
for (int i = 0; i < m; i++) {
if (!edge[i].used) {
int a = edge[i].a;
int b = edge[i].b;
int w = edge[i].w;
res = Math.min(res, sum + lca(a, b, w));
}
}
System.out.println(res);
}
private static int lca(int a, int b, int w) {
int[] distance = new int[N * 2];
int cnt = 0;
if (depth[a] < depth[b]) {
int t = a;
a = b;
b = t;
}
for (int k = 16; k >= 0; k--) {
if (depth[fa[a][k]] >= depth[b]) {
distance[cnt++] = d1[a][k];
distance[cnt++] = d2[a][k];
a = fa[a][k];
}
}
if (a != b) {
for (int k = 16; k >= 0; k--) {
if (fa[a][k] != fa[b][k]) {
distance[cnt++] = d1[a][k];
distance[cnt++] = d2[a][k];
distance[cnt++] = d1[b][k];
distance[cnt++] = d2[b][k];
a = fa[a][k];
b = fa[b][k];
}
}
distance[cnt++] = d1[a][0];
distance[cnt++] = d1[b][0];
}
int dist1 = -INF;
int dist2 = -INF;
for (int i = 0; i < cnt; i++) {
int d = distance[i];
if (d > dist1) {
dist2 = dist1;
dist1 = d;
} else if (d != dist1 && d > dist2) {
dist2 = d;
}
}
if (w > dist1) return w - dist1;
if (w > dist2) return w - dist2;
return INF;
}
private static void bfs() {
Arrays.fill(depth, INF);
depth[0] = 0;
depth[1] = 1;
q[0] = 1;
int hh = 0, tt = 0;
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (depth[j] > depth[t] + 1) {
depth[j] = depth[t] + 1;
q[++tt] = j;
fa[j][0] = t;
d1[j][0] = w[i];
d2[j][0] = -INF;
for (int k = 1; k <= 16; k++) {
int anc = fa[j][k - 1];
fa[j][k] = fa[anc][k - 1];
int[] distance = {d1[j][k - 1], d2[j][k - 1], d1[anc][k - 1], d2[anc][k - 1]};
d1[j][k] = -INF;
d2[j][k] = -INF;
for (int u = 0; u < 4; u++) {
int d = distance[u];
if (d > d1[j][k]) {
d2[j][k] = d1[j][k];
d1[j][k] = d;
} else if (d != d1[j][k] && d > d2[j][k]) {
d2[j][k] = d;
}
}
}
}
}
}
}
private static void build() {
Arrays.fill(h, -1);
for (int i = 0; i < m; i++) {
if (edge[i].used) {
int a = edge[i].a;
int b = edge[i].b;
int w = edge[i].w;
add(a, b, w);
add(b, a, w);
}
}
}
private static long kruskal() {
for (int i = 1; i <= n; i++) {
p[i] = i;
}
Arrays.sort(edge, 0, m, new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.w - o2.w;
}
});
long res = 0;
for (int i = 0; i < m; i++) {
int a = find(edge[i].a);
int b = find(edge[i].b);
int w = edge[i].w;
if (a != b) {
p[a] = b;
res += w;
edge[i].used = true;
}
}
return res;
}
private static int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
private static void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
}
class Edge{
int a;
int b;
int w;
boolean used;
public Edge(int a, int b, int w) {
this.a = a;
this.b = b;
this.w = w;
}
}
例题:352. 闇の連鎖
——————————————————————————————————————————————————
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
static int INF = Integer.MAX_VALUE >> 1;
static int N = 100010;
static int M = N * 2;
static int n;
static int m;
static int[] h = new int[N];
static int[] e = new int[M];
static int[] ne = new int[M];
static int idx;
static int[] depth = new int[N];
static int[][] fa = new int[N][17];
static int[] q = new int[N];
static int[] d = new int[N];
static int ans = 0;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] s1 = br.readLine().split(" ");
n = Integer.parseInt(s1[0]);
m = Integer.parseInt(s1[1]);
Arrays.fill(h, -1);
for (int i = 0; i < n - 1; i++) {
String[] s2 = br.readLine().split(" ");
int a = Integer.parseInt(s2[0]);
int b = Integer.parseInt(s2[1]);
add(a, b);
add(b, a);
}
bfs();
for (int i = 0; i < m; i++) {
String[] s2 = br.readLine().split(" ");
int a = Integer.parseInt(s2[0]);
int b = Integer.parseInt(s2[1]);
int p = lca(a, b);
d[a]++;
d[b]++;
d[p] -= 2;
}
dfs(1, -1);
System.out.println(ans);
}
private static int dfs(int u, int father) {
int res = d[u];
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (j != father) {
int s = dfs(j, u);
if (s == 0) {
ans += m;
} else if (s == 1) {
ans++;
}
res += s;
}
}
return res;
}
private static int lca(int a, int b) {
if (depth[a] < depth[b]) {
int t = a;
a = b;
b = t;
}
for (int k = 16; k >= 0; k--) {
if (depth[fa[a][k]] >= depth[b]) {
a = fa[a][k];
}
}
if (a == b) return a;
for (int k = 16; k >= 0; k--) {
if (fa[a][k] != fa[b][k]) {
a = fa[a][k];
b = fa[b][k];
}
}
return fa[a][0];
}
private static void bfs() {
Arrays.fill(depth, INF);
depth[0] = 0;
depth[1] = 1;
int hh = 0, tt = 0;
q[0] = 1;
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (depth[j] > depth[t] + 1) {
depth[j] = depth[t] + 1;
q[++tt] = j;
fa[j][0] = t;
for (int k = 1; k <= 16; k++) {
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
}
private static void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
}