1. 程式人生 > >HDU4547 CD操作(最近公共祖先lca,離線Tarjan,有坑點)

HDU4547 CD操作(最近公共祖先lca,離線Tarjan,有坑點)

Problem Description

 在Windows下我們可以通過cmd執行DOS的部分功能,其中CD是一條很有意思的命令,通過CD操作,我們可以改變當前目錄。
  這裡我們簡化一下問題,假設只有一個根目錄,CD操作也只有兩種方式:
  
  1. CD 當前目錄名…\目標目錄名 (中間可以包含若干目錄,保證目標目錄通過絕對路徑可達)
  2. CD .. (返回當前目錄的上級目錄)
     現在給出當前目錄和一個目標目錄,請問最少需要幾次CD操作才能將當前目錄變成目標目錄?

Input

輸入資料第一行包含一個整數T(T<=20),表示樣例個數;
每個樣例首先一行是兩個整數N和M(1<=N,M<=100000),表示有N個目錄和M個詢問; 接下來N-1行每行兩個目錄名A
B(目錄名是隻含有數字或字母,長度小於40的字串),表示A的父目錄是B。 最後M行每行兩個目錄名A
B,表示詢問將當前目錄從A變成B最少要多少次CD操作。 資料保證合法,一定存在一個根目錄,每個目錄都能從根目錄訪問到。

Output

請輸出每次詢問的結果,每個查詢的輸出佔一行。

Sample Input

2
3 1
B A
C A
B C

3 2
B A
C B
A C
C A

Sample Output

2
1
2

思路

題意是中文,很好理解,不過有一些需要注意的地方.

  1. 從父目錄到任意一個子目錄的距離都是1
  2. 用一個flag記錄一下詢問是從左到右還是從右到左,有幾種情況
    • 左邊是右邊的父親,距離為1
    • 右邊是左邊的父親,距離是dis[v]到lca(u,v)
    • 其他情況+1

其他就是離線求Tarjan的lca了

程式碼

#include <cstdio>
#include <cstring> #include <cctype> #include <stdlib.h> #include <string> #include <map> #include <iostream> #include <stack> #include <cmath> #include <queue> #include <vector> #include <algorithm> using namespace std; typedef long
long ll; #define inf 1000000 #define mem(a,b) memset(a,b,sizeof(a)) const int N=100000+7; const int M=2*N; int pre[N],first[N],first2[N],tot,tot2; bool vis[N];//標記有沒有詢問 int n; int fa[N],ans[N],cnt; int vis2[N],dis[N]; map<string,int>mp; struct edge { int v,next; } e[M]; struct Query { int v,next,id,flag;//flag用來標記是從左到右還是從到左 } query[M]; void add_edge(int u,int v) { e[tot].v=v; e[tot].next=first[u]; first[u]=tot++; } void add_query(int u,int v,int id,int flag) { query[tot2].flag=flag; query[tot2].id=id; query[tot2].v=v; query[tot2].next=first2[u]; first2[u]=tot2++; } int find(int x) { return x==pre[x]?x:pre[x]=find(pre[x]); } void lca(int u,int fa) { for(int i=first[u]; ~i; i=e[i].next) { int v=e[i].v; if(v==fa) continue; lca(v,u); pre[v]=u; } vis[u]=1; for(int i=first2[u]; ~i; i=query[i].next) { int v=query[i].v; if(vis[v]) { int id=query[i].id; int flag=query[i].flag; if(flag==1) { if(u==find(v)) ans[id]=1; else if(find(u)==v) ans[id]=dis[u]-dis[find(v)]; else ans[id]=dis[u]-dis[find(v)]+1; } else { int u1=v; int v1=u; if(u1==find(v1)) ans[id]=1; else if(find(u1)==v1) ans[id]=dis[u1]-dis[find(v)]; else ans[id]=dis[u1]-dis[find(v)]+1; } } } } void dfs(int u,int len) { vis2[u]=1; dis[u]=len; for(int i=first[u]; ~i; i=e[i].next) { int v=e[i].v; if(!vis2[v]) dfs(v,len+1); } } void init() { mem(first,-1); mem(first2,-1); mem(vis,0); mem(vis2,0); mem(fa,-1); mem(ans,0); tot=0; tot2=0; cnt=1; mp.clear(); for(int i=1; i<=n; i++) pre[i]=i; } struct node { int u,v; } zz[N]; int main() { int t,m; string s1,s2; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); init(); for(int i=1; i<n; i++) { cin>>s1>>s2; if(!mp[s1]) mp[s1]=cnt++; if(!mp[s2]) mp[s2]=cnt++; int a=mp[s1],b=mp[s2]; add_edge(a,b); add_edge(b,a); fa[a]=b; } int root=1; while(~fa[root]) root=fa[root]; dfs(root,0); for(int i=1; i<=m; i++) { cin>>s1>>s2; int a=mp[s1],b=mp[s2]; zz[i].u=a,zz[i].v=b; add_query(a,b,i,1); add_query(b,a,i,2); } lca(root,root); for(int i=1; i<=m; i++) { int u=zz[i].u,v=zz[i].v; if(u==v) puts("0"); else printf("%d\n",ans[i]); } } return 0; }