AHOI2008 聚会

题目传送门


先把每两个点之间的LCA求出来
画图模拟,显然可以(也可能并不显然)发现集合点选为深度最小的LCA时最优

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define mod 998244353
#define LL long long
using namespace std;
LL read() {
    LL k = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
        k = k * 10 + c - 48, c = getchar();
    return k * f;
}
struct zzz {
    int t, nex;
}e[500010 << 1]; int head[500010], tot;
void add(int x, int y) {
    e[++tot].t = y;
    e[tot].nex = head[x];
    head[x] = tot;
}
int f[500010][32], lg[500010], deth[500010];
LL len[500010];
void dfs(int now, int fa) {
    f[now][0] = fa; deth[now] = deth[fa] + 1;
    len[now] = len[fa] + 1;
    for(int i = 1; i <= lg[deth[now]]; ++i)
      f[now][i] = f[f[now][i-1]][i-1];
    for(int i = head[now]; i; i = e[i].nex)
      if(e[i].t != fa) dfs(e[i].t, now);
}
int LCA(int x, int y) {
	//if(x == y) return x;
    if(deth[x] < deth[y]) swap(x, y);
    while(deth[x] > deth[y])
      x = f[x][lg[deth[x]-deth[y]]-1];
    if(x == y) return x;
    for(int i = lg[deth[x]]; i >= 0; --i)
      if(f[x][i] != f[y][i])
        x = f[x][i], y = f[y][i];
    return f[x][0];
}
int main() {
    int n = read(), m = read();
    for(int i = 1; i <= n; ++i) lg[i] = lg[i-1] + (1 << lg[i-1] == i);
    for(int i = 1; i <= n-1; ++i) {
        int x = read(), y = read();
        add(x, y); add(y, x);
    }
    deth[0] = -1; dfs(1, 0);
	for(int i = 1; i <= m; ++i) {
		int x = read(), y = read(), z = read();
		int xx = LCA(x, y), yy = LCA(y, z), zz = LCA(x, z), lca;
		int d = max(deth[xx], max(deth[yy], deth[zz]));
		if(deth[xx] == d) lca = xx;
		else if(deth[yy] == d) lca = yy;
		else lca = zz;
		LL ans = len[x] + len[y] + len[z] - len[xx] - len[yy] - len[zz];
		printf("%d %lld\n", lca, ans);
	}

    return 0;
}
posted @ 2019-11-14 11:09  MorsLin  阅读(108)  评论(0编辑  收藏  举报