タイトル説明
この問題は、長い顔ああです\(qwq \)
:次のように沿ってタイトル
与えられた\(N \)木のノード\(M \)ごとに与えられた時間クエリ、クエリ\ \(X、 Y、\ Z \) 3つのノードは、プログラムツリー内の点を見つける必要が\(P \)
よう\(C = DIST(X、 P)+ DIST(Y、P)+ DIST(Z、P)\) これは、クエリ出力の条件最小値、各時間がかかり\(P \)と最小この時\(C \)
基本的な考え方
ツリーからの問題を参照してください、それは考えることは容易である(LCA \)\が、ここでの三点があり、直接使用することはできません\(LCA \)ので、私たちは少し周りを有効にする必要があります...
三点二十から二間に得られた検討\(LCA、\\)我々はすぐに考えることができる:
\ [P \ \ {LCA(X、Y)、LCA(X、Z)、LCA(Y、Zに)\} \]
証明:
ツリーパスの\((X、Y)\) 、には明らかであろう
[\ FORALL \ P \で\ (x、y)は、DIST(X、P)+ DIST(P、Y)= DISTを(X、Y )\]
そう\(\ P \\)ポイントは影響を与えません\(X、Y \)のコストを、私たちはようにする必要があります\(\ P \\)付近の点\(\ Z \\)のポイント
この時点で、私たちは議論を分類する必要があります。
- \((X、のZ \ Y)、\) 次に\(P = Z \)
- \(Z \ notin(X、 Y)、\) 、次いで
- \(サブツリー内のz \(X)(\)或\(Z \ SubTre(Y))、P = X(\)で或\(P = Y)\)
- \(Z \サブツリーnotin(X)\)且\(Z \サブツリー(Y)、Z = LCA(X、Y notin)\)
私は良いトラブルを感じていないのですか?実際には、我々はあまりをする必要はありません。\(qwq \)
として\(X、Y、Z \ ) の回転で、2点間のみ必要となるよう\(LCA \)を更新しようと\を(P \) 、等ポイント結論の出そうである
上記の議論、自身YYに従って分類することができる(具体的なプロセスは、図を描くことが最良です私は幾何のスケッチブックを使用しませんを教えてくれません。)
詳細注意事項
ツリーパスを計算する場合、直接ではなく必要があります\(DEPの\)が減算するが、それはサンプルWAよりも高速に実行されます
参照コード
#include <cstdio>
#include <cstring>
#define rg register
const int MAXN = 500010;
inline int abs(int a) { return a < 0 ? -a : a; }
inline void swap(int& a, int& b) { int t = a; a = b; b = t; }
inline int read() {
int s = 0; bool f = false; char c = getchar();
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
return f ? -s : s;
}
int tot, head[MAXN], nxt[MAXN << 1], ver[MAXN << 1];
inline void Add_edge(int u, int v) { nxt[++tot] = head[u], head[u] = tot, ver[tot] = v; }
int dep[MAXN], f[MAXN][22];
inline void dfs(int u, int fa) {
dep[u] = dep[fa] + 1, f[u][0] = fa;
for (rg int i = 1; i <= 20; i++)
f[u][i] = f[f[u][i - 1]][i - 1];
for (rg int v, i = head[u]; i; i = nxt[i])
if(!dep[v = ver[i]]) dfs(v, u);
}
inline int LCA(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (rg int i = 20; ~i; --i) if (dep[f[x][i]] >= dep[y]) x = f[x][i];
if (x == y) return x;
for (rg int i = 20; ~i; --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(), q = read();
for (rg int i = 1; i < n; i++) {
int u = read(), v = read();
Add_edge(u, v), Add_edge(v, u);
}
dfs(1, 0);
for (rg int i = 1; i <= q; i++) {
int x = read(), y = read(), z = read();
int pos, minn = 2147483647, c;
//根据轮换性(所以这三段都长得差不多...)
int lca1 = LCA(x, y);
c = dep[x] + dep[y] - 2 * dep[lca1];
int lcaz = LCA(lca1, z);
c += dep[lca1] + dep[z] - 2 * dep[lcaz];
if (minn > c) pos = lca1, minn = c;
int lca2 = LCA(x, z);
c = dep[x] + dep[z] - 2 * dep[lca2];
int lcay = LCA(lca2, y);
c += dep[lca2] + dep[y] - 2 * dep[lcay];
if (minn > c) pos = lca2, minn = c;
int lca3 = LCA(y, z);
c = dep[y] + dep[z] - 2 * dep[lca3];
int lcax = LCA(lca3, x);
c += dep[x] + dep[lca3] - 2 * dep[lcax];
if (minn > c) pos = lca3, minn = c;
printf("%d %d\n", pos, minn);
}
return 0;
}
エンドSahua \(qwq \)