离线LCA的求法,相信大家都知道使用tarjan。该方法确实很巧妙,利用dfs的性质,假设u的父亲为fa,当以u为根节点的子树被访问完之后,那么任何与u同属于同一个父亲fa并且不包含在u的子树内的点,与u子树内的任何一个点的最近公共祖先一定是fa,我们使用并查集维护同属一个ancestor的定点集合。
HDU 2586 How Far Away?
这道题是说给你一棵树,询问任意两点之间的最短距离。
显然dis[u][v] = (dep[u] - dep[lca]) + (dep[v] - dep[lca]),其中dep[u]表示u点到root的距离。
于是先dfs预处理dep数组,然后使用tarjan离线求解u and v之间的lca,并且记录结果。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int NMAX = 40010;
typedef struct NODE
{
int v, dis;
NODE(int tv, int tdis)
{
v = tv;
dis = tdis;
}
}Node;
vector<Node> adj[NMAX];
typedef struct QUERY_NODE
{
int v, index;
QUERY_NODE(int tv, int tindex)
{
v = tv;
index = tindex;
}
}QueryNode;
vector<QueryNode> query[NMAX];
int ancestor[NMAX];
int tree[NMAX];
bool vis[NMAX];
int dep[NMAX];
int find(int u)
{
int r = u;
while(tree[r] != r)
{
r = tree[r];
}
while(u != r)
{
int temp = tree[u];
tree[u] = r;
u = temp;
}
return r;
}
void init()
{
memset(vis, false, sizeof(vis));
for(int i=0; i<NMAX; i++)
{
adj[i].clear();
query[i].clear();
}
}
void tarjan(int fa, int u)
{
tree[u] = u;
//vis[u] = true;
int len = adj[u].size();
for(int i=0; i<len; i++)
{
int v = adj[u][i].v;
if(v != fa)
{
tarjan(u, v);
tree[find(v)] = u;
}
}
vis[u] = true;
len = query[u].size();
for(int i=0; i<len; i++)
{
int v = query[u][i].v;
int index = query[u][i].index;
if(vis[v])
{
ancestor[index] = find(v);
int lca = ancestor[index];
ancestor[index] = (dep[u]-dep[lca]) + (dep[v] - dep[lca]);
//printf("%d %d %d %d\n", u, v, lca, ancestor[index]);
}
}
}
int getDis(int fa, int u)
{
int len = adj[u].size();
for(int i=0; i<len; i++)
{
int v = adj[u][i].v;
int dis = adj[u][i].dis;
if(v != fa)
{
dep[v] = dep[u] + dis;
getDis(u, v);
}
}
}
int main()
{
//freopen("data.in", "r", stdin);
int T;
scanf("%d", &T);
int n, k;
while(T--)
{
scanf("%d%d", &n, &k);
init();
int u, v, w;
for(int i=1; i<n; i++)
{
scanf("%d%d%d", &u, &v, &w);
adj[u].push_back(Node(v, w));
adj[v].push_back(Node(u, w));
}
for(int i=0; i<k; i++)
{
scanf("%d%d", &u, &v);
query[u].push_back(QueryNode(v, i));
query[v].push_back(QueryNode(u, i));
}
memset(dep, 0, sizeof(dep));
getDis(-1, 1);
tarjan(-1, 1);
for(int i=0; i<k; i++)
printf("%d\n", ancestor[i]);
}
return 0;
}
HDU 2874 connections between cities
这道题何上一题差不多,题意是说:给了你很多棵树,询问任意两点:如果不在同一棵树,那么输出not connected;否则输出两点之间的距离,和上一题做法一样。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int NMAX = 10010;
typedef struct NODE
{
int v, dis;
NODE(int tv, int tdis)
{
v = tv;
dis = tdis;
}
}Node;
vector<Node> adj[NMAX];
typedef struct QUERY_NODE
{
int v, index;
QUERY_NODE(int tv, int tindex)
{
v = tv;
index = tindex;
}
}QueryNode;
vector<QueryNode> query[NMAX];
int ancestor[1000006];
int tree[NMAX];
bool vis[NMAX], visited[NMAX];
int dep[NMAX];
int find(int u)
{
int r = u;
while(tree[r] != r)
{
r = tree[r];
}
while(u != r)
{
int temp = tree[u];
tree[u] = r;
u = temp;
}
return r;
}
void init()
{
memset(visited, false, sizeof(visited));
for(int i=0; i<NMAX; i++)
{
adj[i].clear();
query[i].clear();
}
}
void tarjan(int fa, int u)
{
visited[u] = true;
tree[u] = u;
int len = adj[u].size();
for(int i=0; i<len; i++)
{
int v = adj[u][i].v;
if(v != fa)
{
tarjan(u, v);
tree[find(v)] = u;
}
}
vis[u] = true;
len = query[u].size();
for(int i=0; i<len; i++)
{
int v = query[u][i].v;
int index = query[u][i].index;
if(vis[v])
{
ancestor[index] = find(v);
int lca = ancestor[index];
ancestor[index] = (dep[u]-dep[lca]) + (dep[v] - dep[lca]);
//printf("%d %d %d %d\n", u, v, lca, ancestor[index]);
}
}
}
int getDis(int fa, int u)
{
int len = adj[u].size();
for(int i=0; i<len; i++)
{
int v = adj[u][i].v;
int dis = adj[u][i].dis;
if(v != fa)
{
dep[v] = dep[u] + dis;
getDis(u, v);
}
}
}
int main()
{
//freopen("data.in", "r", stdin);
int n, m, k;
while(scanf("%d%d%d", &n, &m, &k) != EOF)
{
init();
int u, v, w;
for(int i=0; i<m; i++)
{
scanf("%d%d%d", &u, &v, &w);
adj[u].push_back(Node(v, w));
adj[v].push_back(Node(u, w));
}
for(int i=0; i<k; i++)
{
scanf("%d%d", &u, &v);
query[u].push_back(QueryNode(v, i));
query[v].push_back(QueryNode(u, i));
}
memset(dep, 0, sizeof(dep));
memset(ancestor, -1, sizeof(ancestor));
for(int i=1; i<=n; i++)
{
if(!visited[i])
{
memset(vis, false, sizeof(vis));
getDis(-1, i);
tarjan(-1, i);
}
}
for(int i=0; i<k; i++)
{
if(ancestor[i] == -1)
printf("Not connected\n");
else
printf("%d\n", ancestor[i]);
}
}
return 0;
}
HDU 4547 CD操作
这道大水题,被坑爹了。输入的查询中存在之前目录中没有出现的点,然后就是输入肯定是保证这两个没有出现过的点相同,所以初始化的时候将答案全初始化为0就行了。
坑爹了……
奉上代码吧。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <map>
using namespace std;
const int MAX = 100010;
vector<int> adj[MAX];
vector<int> queryid[MAX];
int dep[MAX], ans[MAX];
bool vis[MAX];
typedef struct QUERY
{
int a, b;
QUERY(){}
QUERY(int ta, int tb)
{
a = ta;
b = tb;
}
}Query;
Query query[MAX];
map<string, int> hash_name;
void tarjan(int root);
void init(int n, int m)
{
memset(vis, false, sizeof(vis));
hash_name.clear();
for(int i=1; i<MAX; i++)
{
adj[i].clear();
queryid[i].clear();
}
char sa[50], sb[50];
int cnt = 0;
int u, v;
for(int i=1; i<n; i++)
{
scanf("%s %s", sa, sb);
if(hash_name[sa] == 0)
hash_name[sa] = ++cnt;
u = hash_name[sa];
if(hash_name[sb] == 0)
hash_name[sb] = ++cnt;
v = hash_name[sb];
adj[v].push_back(u);
vis[u] = true;
}
for(int i=0; i<m; i++)
{
scanf("%s %s", sa, sb);
if(hash_name[sa] == 0)
hash_name[sa] = ++cnt;
u = hash_name[sa];
if(hash_name[sb] == 0)
hash_name[sb] = ++cnt;
v = hash_name[sb];
query[i] = Query(u, v);
queryid[u].push_back(i);
queryid[v].push_back(i);
}
memset(ans, 0, sizeof(ans));
int root;
for(int i=1; i<=n; i++)
{
if(!vis[i])
{
root = i;
break;
}
}
memset(vis, false, sizeof(vis));
tarjan(root);
}
int tree[MAX];
int find(int u)
{
int root = u;
while(tree[root] != root)
root = tree[root];
while(u != root)
{
int temp = tree[u];
tree[u] = root;
u = temp;
}
return root;
}
void tarjan(int u)
{
int len = adj[u].size();
tree[u] = u;
for(int i=0; i<len; i++)
{
int v = adj[u][i];
dep[v] = dep[u]+1;
tarjan(v);
tree[find(v)] = u;
}
vis[u] = true;
len = queryid[u].size();
for(int i=0; i<len; i++)
{
int id = queryid[u][i];
int a = query[id].a;
int b = query[id].b;
int lca;
if(u == a)
lca = find(b);
else
lca = find(a);
ans[id] = dep[a] - dep[lca];
if(lca != b)
ans[id]++;
}
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n, m;
scanf("%d%d", &n, &m);
init(n, m);
for(int i=0; i<m; i++)
printf("%d\n", ans[i]);
}
return 0;
}