您的位置:首页 > 其它

BZOJ1787: [Ahoi2008]Meet 紧急集合(LCA)

2016-04-11 18:19 260 查看
这一道题中,每一次询问的答案就是这三个点,两两的LCA的异或和。至于这样为什么是对的呢?

我们先考虑是否一定有重复的LCA呢?

如果三个点在根节点的不同子树中,那么LCA都是根节点,三个都重复。

如果有两个在一个子树中,那么这两个与另外一个的LCA都是根节点。

如果三个都在一个子树中,可以归纳到以上两种情况。

然后考虑为什么是异或和。

如果三个LCA相同,那么一定是这个点无疑,等于异或和

如果有一个LCA与另外两个不同的时候,肯定要尽量避免两个人走同一段路。因为如果要走到三个点都在的深度最大的子树的根,即相同的LCA,那么必定会有两个人走过同一段路,所以去另一个,每个点都不会走重复的路径

/**************************************************************
Problem: 1787
User: geng4512
Language: C++
Result: Accepted
Time:1880 ms
Memory:20336 kb
****************************************************************/

#include <cstdio>
#define MAXN 500005
struct Node { int v, nxt; } e[MAXN << 1];
int hsn[MAXN], htp[MAXN], sz[MAXN];
int n, m, c, fa[MAXN], dep[MAXN], adj[MAXN];
inline void Add(int u, int v) { ++ c; e[c].v = v; e[c].nxt = adj[u]; adj[u] = c; }
inline void GET(int &n) {
static char c; n = 0; do c = getchar(); while('0' > c || c > '9');
do n=n*10+c-'0',c=getchar(); while('0' <= c && c <= '9');
}
void dfs1(int u) {
sz[u] = 1;
for(int i = adj[u]; i; i = e[i].nxt) {
if(sz[e[i].v]) continue;
dep[e[i].v] = dep[u] + 1;
fa[e[i].v] = u;
dfs1(e[i].v);
sz[u] += sz[e[i].v];
if(sz[e[i].v] > sz[hsn[u]]) hsn[u] = e[i].v;
}
}
void dfs2(int u, int tp) {
htp[u] = tp;
if(hsn[u]) dfs2(hsn[u], tp);
for(int i = adj[u]; i; i = e[i].nxt)
if(!htp[e[i].v]) dfs2(e[i].v, e[i].v);
}
inline int LCA(int u, int v) {
while(htp[u] != htp[v]) {
if(dep[htp[u]] > dep[htp[v]]) u = fa[htp[u]];
else v = fa[htp[v]];
}
return dep[u] > dep[v] ? v : u;
}
inline int dis(int u, int v) { return dep[u] - 2*dep[LCA(u, v)] + dep[v]; }
int main() {
scanf("%d%d", &n, &m); int u, v;
for(int i = 1; i < n; ++ i) {
GET(u); GET(v);
Add(u, v); Add(v, u);
}
dfs1(1); dfs2(1, 1);
int x, y, z, a, b, c;
for(int i = 1; i <= m; ++ i) {
GET(a); GET(b); GET(c); x = LCA(a, b);
y = LCA(b, c); z = LCA(a, c);
z = x ^ y ^ z;
printf("%d %d\n", z, dis(a, z) + dis(b, z) + dis(c, z));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: