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 }