2014-10-26 12:13:10
思路:lca启蒙题,比较裸的一题,水过吧。有离线Tarjan和在线倍增两种做法。
离线Tarjan:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /************************************************************************* 2 > File Name: 1470.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Sat 25 Oct 2014 06:50:04 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <stack> 16 #include <queue> 17 #include <iostream> 18 #include <algorithm> 19 using namespace std; 20 #define lp (p << 1) 21 #define rp (p << 1|1) 22 #define getmid(l,r) (l + (r - l) / 2) 23 #define MP(a,b) make_pair(a,b) 24 typedef long long ll; 25 const int INF = 1 << 30; 26 const int maxn = 1000; 27 28 int n,m; 29 int first[maxn],next[maxn * maxn],ver[maxn * maxn],ecnt; 30 int fa[maxn]; 31 int que[maxn][maxn]; 32 int vis[maxn]; 33 int cnt[maxn],f[maxn]; 34 35 int Find(int x){ 36 return fa[x] == x ? x : fa[x] = Find(fa[x]); //并查集find,路径压缩 37 } 38 39 void Union(int u,int v){ 40 int x = Find(u); 41 int y = Find(v); 42 if(x != y) 43 fa[y] = x; //后者以前者为祖先 44 } 45 46 void Add_edge(int u,int v){ 47 next[++ecnt] = first[u]; 48 ver[ecnt] = v; 49 first[u] = ecnt; 50 } 51 52 void Init(){ 53 memset(f,0,sizeof(f)); 54 memset(cnt,0,sizeof(cnt)); 55 memset(que,0,sizeof(que)); 56 memset(vis,0,sizeof(vis)); 57 memset(first,-1,sizeof(first)); 58 ecnt = 0; 59 } 60 61 void Tarjan(int p){ 62 fa[p] = p; //先把遍历到的点p自己加入一个集合 63 for(int i = first[p]; i != -1; i = next[i]){ 64 int v = ver[i]; 65 Tarjan(v); //遍历子节点 66 Union(p,v); //因为p是其所有子节点的祖先,所以并进集合,并以p为祖先 67 } 68 vis[p] = 1; //表示以p为根的整棵子树已经访问完毕 69 for(int i = 1; i <= n; ++i) if(que[p][i] && vis[i]){ 70 cnt[Find(i)] += que[p][i]; 71 //询问的是(p,i),如果i已经访问过,那么说明遍历到lca(p,i)时 72 //是先遍历到包含i的子树,再遍历包含p的子树。那么i当前的最祖先节点必然 73 //是p和i的最近公共祖先(因为i的最祖先节点是不断更新的,一旦能发现p点, 74 //那么当前最祖先节点是最近的。) 75 76 //que[p][i] = que[i][p] = 0; 77 //上句话其实没有必要,因为一对关系只会求一次。每次关系会处理到 78 //两次,必定有一次是只有一个点遍历过,另一个点没有遍历过 79 } 80 } 81 82 int main(){ 83 int a,b,k; 84 while(scanf("%d",&n) != EOF){ 85 Init(); 86 for(int i = 1; i <= n; ++i){ 87 scanf("%d:(%d)",&a,&k); 88 while(k--){ 89 scanf("%d",&b); 90 Add_edge(a,b); //单条有向边 91 f[b] = 1; //用于区别出根节点与其他节点 92 } 93 } 94 char c; 95 scanf("%d",&m); 96 for(int i = 1; i <= m; ++i){ 97 c = getchar(); 98 while(c != '(') c = getchar(); 99 scanf("%d%d",&a,&b); 100 c = getchar(); 101 while(c != ')') c = getchar(); 102 que[a][b]++; //对称地记录询问 103 que[b][a]++; 104 } 105 for(int i = 1; i <= n; ++i) 106 if(!f[i]){ 107 Tarjan(i); //i就为整棵树的根节点 108 break; 109 } 110 for(int i = 1; i <= n; ++i) if(cnt[i]){ 111 printf("%d:%d\n",i,cnt[i]); 112 } 113 } 114 return 0; 115 }
在线倍增(后学):
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define REV(i,n) for(int i=(n);i>=1;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 21 #define MP(a,b) make_pair(a,b) 22 23 typedef long long ll; 24 typedef pair<int,int> pii; 25 const int INF = (1 << 30) - 1; 26 const int MAXN = 1010; 27 const int MAX_LOG = 10; 28 29 int n,m,first[MAXN],ecnt,f[MAXN],root; 30 int fa[11][MAXN],dep[MAXN]; 31 int lca[MAXN]; 32 33 struct edge{ 34 int v,next; 35 }e[MAXN * MAXN]; 36 37 void Add_edge(int u,int v){ 38 e[++ecnt].next = first[u]; 39 e[ecnt].v = v; 40 first[u] = ecnt; 41 } 42 43 void Dfs(int p,int pre,int d){ 44 fa[0][p] = pre; 45 dep[p] = d; 46 for(int i = first[p]; ~i; i = e[i].next){ 47 int v = e[i].v; 48 if(v == pre) continue; 49 Dfs(v,p,d + 1); 50 } 51 } 52 53 void Init(){ 54 Dfs(root,-1,0); 55 for(int k = 0; k + 1 < MAX_LOG; ++k){ 56 for(int i = 1; i <= n; ++i){ 57 if(fa[k][i] < 0) fa[k + 1][i] = -1; 58 else fa[k + 1][i] = fa[k][fa[k][i]]; 59 } 60 } 61 } 62 63 void Lca(int u,int v){ 64 if(dep[u] > dep[v]) swap(u,v); 65 for(int k = 0; k < MAX_LOG; ++k){ 66 if((dep[v] - dep[u]) & (1 << k)) 67 v = fa[k][v]; 68 } 69 if(u == v){ 70 lca[u]++; 71 return; 72 } 73 for(int k = MAX_LOG - 1; k >= 0; --k){ 74 if(fa[k][u] != fa[k][v]){ 75 u = fa[k][u]; 76 v = fa[k][v]; 77 } 78 } 79 lca[fa[0][u]]++; 80 } 81 82 int main(){ 83 char c; 84 int a,b,k; 85 while(scanf("%d",&n) != EOF){ 86 MEM(first,-1); 87 MEM(f,0); 88 MEM(lca,0); 89 ecnt = 0; 90 REP(i,n){ 91 scanf("%d:(%d)",&a,&k); 92 while(k--){ 93 scanf("%d",&b); 94 Add_edge(a,b); 95 f[b] = 1; 96 } 97 } 98 REP(i,n) if(!f[i]){ 99 root = i; 100 break; 101 } 102 Init(); 103 scanf("%d",&m); 104 REP(i,m){ 105 c = getchar(); 106 while(c != '(') c = getchar(); 107 scanf("%d%d",&a,&b); 108 while(c != ')') c = getchar(); 109 Lca(a,b); 110 } 111 REP(i,n) if(lca[i]) 112 printf("%d:%d\n",i,lca[i]); 113 } 114 return 0; 115 }