【题目链接】
【算法】
分四种情况讨论 :
1. 当前目录和目标目录是同一目录,不需要变换,答案为0
2. 当前目录是目标目录的祖先,答案为当前目录的深度 - 目标目录的深度
3. 当前目录是目标目录的孩子,答案为1
4. 当前目录和目标目录有最近公共祖先,答案为当前目录的深度 - 最近公共祖先的深度 + 1
那么,算法就很明确了,先做一遍离线tarjan,求出询问点对的最近公共祖先,然后,进行上述的分类讨论
【代码】
由于目录名称是字符串,笔者用stl库里的map将这些目录重新编号
#include<bits/stdc++.h> using namespace std; #define MAXN 100010 #define MAXL 45 struct Edge { int to,nxt; } e[MAXN]; int i,T,tot,id,n,m,root; int head[MAXN],dep[MAXN],f[MAXN],lca[MAXN],fa[MAXN]; string a,b; string x[MAXN],y[MAXN]; bool visited[MAXN]; map<string,int> mp; vector< pair<int,int> > query[MAXN]; inline void add(int u,int v) { tot++; e[tot] = (Edge){v,head[u]}; head[u] = tot; } inline void init(int u) { int i,v; for (i = head[u]; i; i = e[i].nxt) { v = e[i].to; dep[v] = dep[u] + 1; init(v); } } inline int find(int x) { if (f[x] == x) return x; return f[x] = find(f[x]); } inline void tarjan(int u) { int i,v,pos; visited[u] = true; f[u] = u; for (i = 0; i < query[u].size(); i++) { v = query[u][i].first; pos = query[u][i].second; if (visited[v]) lca[pos] = find(v); } for (i = head[u]; i; i = e[i].nxt) { v = e[i].to; tarjan(v); f[v] = u; } } int main() { ios :: sync_with_stdio(0); cin.tie(0); cin >> T; while (T--) { cin >> n >> m; tot = id = 0; mp.clear(); for (i = 1; i <= n; i++) { head[i] = 0; fa[i] = 0; visited[i] = false; query[i].clear(); } for (i = 1; i < n; i++) { cin >> a >> b; if (!mp[a]) mp[a] = ++id; if (!mp[b]) mp[b] = ++id; add(mp[b],mp[a]); fa[mp[a]] = mp[b]; } for (i = 1; i <= n; i++) { if (!fa[i]) root = i; } dep[root] = 0; init(root); for (i = 1; i <= m; i++) { cin >> x[i] >> y[i]; query[mp[x[i]]].push_back(make_pair(mp[y[i]],i)); query[mp[y[i]]].push_back(make_pair(mp[x[i]],i)); } tarjan(root); for (i = 1; i <= m; i++) { if (x[i] == y[i]) printf("%d ",0); else if (lca[i] == mp[x[i]]) printf("%d ",1); else if (lca[i] == mp[y[i]]) printf("%d ",dep[mp[x[i]]]-dep[mp[y[i]]]); else printf("%d ",dep[mp[x[i]]]-dep[lca[i]]+1); } } return 0; }