题目——最近公共祖先
Tagjan
将所有的查询存起来
然后一遍dfs,得出所有LCA
非常奇妙
主要依据于以下操作
13 和 14 的 LCA 是7
当dfs到7时 (color{pink}{模拟}) 断开 3 - 7
然后搜左子树
vis[13] = 1
再搜右子树时
搜到14 并从此继续搜索 回溯后检查发现vis[13] = 1 update答案
也可以说
最近公共LCA以下 两结点不在同一子树
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline void Read(int &x)
{
x = 0;
char a = getchar();
bool f = 0;
while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
if(f) x *= -1;
}
const int MAXN = 500001;
vector<int> G[MAXN];
vector<pair<int,int> > Ask[500001];
//存询问
int f[MAXN],ans[MAXN];
/*
f用并查集来维护加删边
ans用于统一输出
*/
bool vis[MAXN],Get[MAXN];
inline int Find_set(int x)
{
if(f[x] == x) return x;
return f[x] = Find_set(f[x]);
}
inline void Union(int u,int v)
{
int A1 = Find_set(u),A2 = Find_set(v);
if(A1 != A2) f[A1] = A2;
}
//并查集的标准操作
inline void Tagjan(int x)
{
int i;
for(i = 0;i < G[x].size();i++)
{
if(!vis[G[x][i]])
{
vis[G[x][i]] = 1;
Tagjan(G[x][i]);
Union(G[x][i],x);
/*先Tagjan下去,再连边
因为要模拟断边
*/
}
}
//遍历整个子树
for(i = 0;i < Ask[x].size();i++)
{
int v = Ask[x][i].first,Index = Ask[x][i].second;
if(vis[v]) ans[Index] = Find_set(v);
/*因为u已经遍历到了
v在其他子树的话,上层断边,确保ans
v就在此子树的话,u已断边,v必搜到u
*/
}
}
int main()
{
int i,n,m,s;
Read(n),Read(m),Read(s);
for(i = 1;i < n;i++)
{
f[i] = i;
int u,v;
Read(u),Read(v);
G[u].push_back(v);
G[v].push_back(u);
}
for(i = 1;i <= m;i++)
{
int u,v;
Read(u),Read(v);
Ask[u].push_back(make_pair(v,i));
Ask[v].push_back(make_pair(u,i));
//两边都要push进去,因为不清楚谁更浅
}
vis[s] = 1;
//初始一定要清!!!!!
Tagjan(s);
for(i = 1;i <= m;i++)
printf("%d
",ans[i]);
return 0;
}
Another Way
线段树维护,又依据于以下原则
有一种叫dfs序的东西 把上图的4,3,7,5看成一棵树(to be convenient
dfs序为3437353
那任两结点的公共祖先就在上序的两对应值间
如7和4 ->3
但这还不足以用线段树来维护
就新增数组
dep[i] 记录i的深度
就变成了一个静态区间查询问题
显然 还可用其它数据结构
这里洛谷的过了点这
然而没有吸氧 只是有个点差两毫秒TLE啊
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline void Read(int &x)
{
x = 0;
char a = getchar();
bool f = 0;
while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
if(f) x *= -1;
}
const int MAXN = 500001;
int tot,seq[MAXN << 1],Index[MAXN],dep[MAXN + 2];
//seq[]记录dfs序,Index记录结点在dfs序的位子,dep深度(说过了
vector<int> G[MAXN];
bool vis[MAXN];
inline void dfs(int x)
{
seq[++tot] = x;
Index[x] = tot;
for(int i = 0;i < G[x].size();i++)
{
if(!vis[G[x][i]])
{
vis[G[x][i]] = 1;
dep[G[x][i]] = dep[x] + 1;
dfs(G[x][i]);
seq[++tot] = x;
}
}
}
template<typename T>
inline T Min(T a,T b) {if(a < b) return a;return b;}
template<typename T>
inline void exchange(T &a,T &b) {T c = a;a = b;b = c;}
int tree[MAXN << 3],Left,Right;
//tree[MAXN << 3]一定是3,因为是针对seq[MAXN << 1]的
inline void tree_build(int l,int r,int k)
{
if(l == r)
{
tree[k] = seq[l];
return;
}
int mid = l + r >> 1;
tree_build(l,mid,k << 1);
tree_build(mid + 1,r,k << 1 ^ 1);
if(dep[tree[k << 1]] < dep[tree[k << 1 ^ 1]])
tree[k] = tree[k << 1];
else tree[k] = tree[k << 1 ^ 1];
//是比较深度,不是比较编号大小
}
inline int query(int l,int r,int k)
{
if(Left <= l&&r <= Right)
return tree[k];
int mid = l + r >> 1,A1,A2;
A1 = A2 = MAXN + 1;
//main中写了dep[MAXN + 1] = 极大值
if(Left <= mid)
A1 = query(l,mid,k << 1);
if(mid < Right)
A2 = query(mid + 1,r,k << 1 ^ 1);
if(dep[A1] < dep[A2])
return A1;
return A2;
}
int main()
{
dep[MAXN + 1] = 5000011;
int n,m,i,S;
Read(n),Read(m);
Read(S);
//它给定根
for(i = 1;i <= n - 1;i++)
{
int u,v,w;
Read(u),Read(v);
G[u].push_back(v);
G[v].push_back(u);
}
vis[S] = 1;
dfs(S);
tree_build(1,tot,1);
while(m--)
{
int u,v;
Read(u),Read(v);
Left = Index[u],Right = Index[v];
if(Left > Right) exchange(Left,Right);
//可能顺序不对
int LCA = query(1,tot,1);
printf("%d
",LCA);
}
return 0;
}
延伸
如果求 u 到 v 的最短距离
可以变成
power[u] + power[v] - 2 * power[LCA(u,v)]
倍增
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline void Read(int &x)
{
x = 0;
char a = getchar();
bool f = 0;
while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
if(f) x *= -1;
}
const int MAXN = 50001,MAXK = 50;
int dep[MAXN],f[MAXN][MAXK];
long long power[MAXN],all;
bool vis[MAXN];
vector<int> G[MAXN],Road[MAXN];
inline void dfs(int u)
{
for(int i = 0;i < G[u].size();i++)
{
if(!vis[G[u][i]])
{
vis[G[u][i]] = 1;
dep[G[u][i]] = dep[u] + 1;
f[G[u][i]][0] = u;
power[G[u][i]] = power[u] + Road[u][i];
dfs(G[u][i]);
}
}
}
inline void adjust(int &u,int val)
{
for(int i = MAXK - 1;i >= 0;i--)
if(dep[f[u][i]] >= val)
u = f[u][i];
}
inline int lca(int u,int v)
{
if(dep[u] > dep[v]) adjust(u,dep[v]);
else if(dep[u] < dep[v]) adjust(v,dep[u]);
if(u == v) return u;
for(int i = MAXK - 1;i >= 0;i--)
if(f[u][i] != f[v][i])
u = f[u][i],v = f[v][i];
return f[u][0];
}
inline void count(int u,int v)
{
int w = lca(u,v);
all += power[u] + power[v] - power[w] * 2;
}
int main()
{
int n;
bool F = 0;
while(~scanf("%d",&n))
{
if(F) putchar('
');
F = 1;
int i,m;
memset(vis,0,sizeof(vis));
memset(dep,0,sizeof(dep));
memset(f,0,sizeof(f));
memset(power,0,sizeof(power));
for(i = 1;i <= n;i++) G[i].clear(),Road[i].clear();
for(i = 1;i < n;i++)
{
int u,v,w;
Read(u),Read(v),Read(w);
u++,v++;
G[u].push_back(v);
G[v].push_back(u);
Road[u].push_back(w);
Road[v].push_back(w);
}
vis[1] = 1;
dfs(1);
f[1][0] = 1;
for(i = 1;i < MAXK;i++)
for(int j = 1;j <= n;j++)
f[j][i] = f[f[j][i - 1]][i - 1];
Read(m);
while(m--)
{
int a,b,c;
Read(a),Read(b),Read(c);
a++,b++,c++;
all = 0;
count(a,b),count(b,c),count(a,c);
printf("%lld
",all / 2);
}
}
return 0;
}