LCA:最近公共祖先。
算法:在线倍增算法,将问题转化为RMQ问题(序列极值问题)使用ST算法进行求解,ST算法的思想是两两比较,在两两比较的基础上四四比较,在四四比较的基础上八八比较(二进制的思想)。时间复杂度。
本来是做的HDU 4547 但是不知道为啥就是AC不了,很难受。所以改为这个题目。(有点莫名其妙)也同时附上4547的代码供一起讨论。
配题:HDU 2585
题意:n个村庄,之间都是绝对路径,给出一些列查询,输出两个村庄之间的距离。可以用floyd最短路,但是会超时,所以此时可以使用LCA在线算法解决该问题,因为是只有一条路径。
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn = 40005;
int n, m;
struct NODE
{
int v;
int w;
};
vector<NODE>graph[maxn];
int dis[maxn];
int depth[maxn*2];
int dp[maxn*2][20];
bool vis[maxn];
int fst[maxn];
int ver[maxn*2];
int tot;
void lca_dfs(int cur_node, int deep)
{
vis[cur_node] = true;
fst[cur_node] = ++tot;
ver[tot] = cur_node;
depth[tot] = deep;
for (int i = 0; i < graph[cur_node].size(); i++)
{
int next_node = graph[cur_node][i].v;
int w = graph[cur_node][i].w;
if (vis[next_node]) continue;
dis[next_node] = dis[cur_node] + w;
lca_dfs(next_node, deep+1);
depth[++tot] = deep;
ver[tot] = cur_node;
}
}
int MIN(int a, int b)
{
if (depth[a] < depth[b])
{
return a;
}
else
{
return b;
}
}
void lca_st()
{
for (int i = 1; i <= tot; i++)
{
dp[i][0] = i;
}
for (int j = 1; (1<<j) <= tot; j++)
{
for (int i = 1; i + (1<<j) - 1 <= tot; i++)
{
dp[i][j] = MIN(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}
}
}
int lca_query(int st, int ed)
{
st = fst[st];
ed = fst[ed];
if (st > ed) swap(st, ed);
int k = (int)log2(ed - st + 1);
return ver[MIN(dp[st][k], dp[ed - (1<<k) + 1][k])];
}
int main()
{
int T;
cin>> T;
while (T--)
{
tot = 0;
cin>> n>> m;
for (int i = 1; i <= n; i++)
{
graph[i].clear();
}
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n-1; i++)
{
int a, b, w;
struct NODE node;
scanf("%d %d %d", &a, &b, &w);
node.v = b;
node.w = w;
graph[a].push_back(node);
node.v = a;
graph[b].push_back(node);
}
dis[1] = 0;
lca_dfs(1, 1);
lca_st();
for (int i = 1; i <= m; i++)
{
int a, b;
scanf("%d %d", &a, &b);
int lca_id = lca_query(a, b);
cout<< dis[a] + dis[b] - 2*dis[lca_id]<< endl;
}
}
return 0;
}
附件:HDU 4547(中文题意),疯狂WA的代码,很难受,望指教。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
const int maxN = 100005;
vector<int>graph[maxN];
map<string, int>maplist;
int N, M;
bool degree[maxN];
int fst[maxN], depth[maxN*2], tot;
bool vis[maxN];
int dp[maxN*2][20];
int cnt;
int MIN(const int a, const int b)
{
if (depth[a] < depth[b]) return a;
else return b;
}
void lca_dfs(int cur_node, int deep)
{
vis[cur_node] = true;
fst[cur_node] = ++tot;
depth[tot] = deep;
for (int i = 0; i < graph[cur_node].size(); i++)
{
int next_node = graph[cur_node][i];
if (vis[next_node]) continue;
lca_dfs(next_node, deep + 1);
depth[++tot] = deep;
}
}
void RMQ()
{
for (int i = 1; i <= tot; i++)
{
dp[i][0] = i;
}
for (int j = 1; (1 << j) <= tot; j++)
{
for (int i = 1; i + (1 << j) - 1 <= tot; i++)
{
dp[i][j] = MIN(dp[i][j-1], dp[i + (1<<j)][j-1]);
}
}
}
int query_RMQ(int st, int ed)
{
if (st > ed)
{
swap(st, ed);
}
int k = (int)log2(ed - st + 1);
return MIN(dp[st][k], dp[ed - (1<<k) + 1][k]);
}
int main()
{
int T;
cin>> T;
while (T--)
{
int root;
tot = 0;
cnt = 0;
scanf("%d %d", &N, &M);
maplist.clear();
memset(vis, false, sizeof(vis));
memset(degree, false, sizeof(degree));
for (int i = 0; i <= N ; i++)
{
graph[i].clear();
}
for (int i = 1; i <= N-1; i++)
{
char a[45], b[45];
scanf("%s %s", a, b);
if (!maplist[a]) maplist[a] = ++cnt;
if (!maplist[b]) maplist[b] = ++cnt;
graph[maplist[b]].push_back(maplist[a]);
degree[maplist[a]] = true;
}
for (int i = 1; i <= cnt; i++)
{
if (degree[i] == false)
{
root = i;
break;
}
}
lca_dfs(root, 1);
RMQ();
for (int i = 1; i <= M; i++)
{
char a[45], b[45];
scanf("%s %s", a, b);
int u = fst[maplist[a]];
int v = fst[maplist[b]];
if (N == 1 || u == v)
{
cout<< 0<< endl;
continue;
}
int lca_id = query_RMQ(u, v);
cout<< depth[u] - depth[lca_id] + (v != lca_id)<< endl;
}
}
return 0;
}