2014-10-26 12:13:10

思路:lca启蒙题,比较裸的一题,水过吧。有离线Tarjan和在线倍增两种做法。

离线Tarjan:

 

  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 }
View Code

相关文章: