POJ 1330 Nearest Common Ancestors LCA
题目链接:
http://poj.org/problem?id=1330
题意:
给你一颗有根树,最后输入一对数(a,b),叫你求a和b的公共祖先。
裸的lca,数据也很小,拿来练手不错。
题解:
1、tarjan_lca,离线,线性时间复杂度
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 7 const int maxn = 1e4 + 10; 8 9 int n,root; 10 int deg[maxn],vis[maxn],p[maxn]; 11 vector<int> tree[maxn],que[maxn]; 12 13 void init() { 14 memset(deg, 0, sizeof(deg)); 15 for (int i = 0; i <= n; i++) tree[i].clear(); 16 for (int i = 0; i <= n; i++) que[i].clear(); 17 memset(vis, 0, sizeof(vis)); 18 root = -1; 19 } 20 //并查集: 21 int fin(int x) { return p[x] = p[x] == x ? x : fin(p[x]); } 22 void uni(int a, int b) { 23 int pa = fin(a); 24 int pb = fin(b); 25 //不能写成p[pa]=pb,和tarjan算法有关; 26 p[pb] = pa; 27 } 28 29 bool tarjan_LCA(int rt,int& ans) { 30 p[rt] = rt; 31 for (int i = 0; i < tree[rt].size(); i++) { 32 int v = tree[rt][i]; 33 if (tarjan_LCA(v, ans)) return true; 34 //把v并到rt上 35 uni(rt, v); 36 } 37 //涂黑这一点 38 vis[rt] = 1; 39 //处理与rt有关的查询 40 for (int i = 0; i < que[rt].size(); i++) { 41 int qv = que[rt][i]; 42 if (vis[qv]) { 43 ans = fin(qv); 44 return true; 45 } 46 } 47 return false; 48 } 49 50 int main() { 51 int tc; 52 scanf("%d", &tc); 53 while (tc--) { 54 scanf("%d", &n); 55 init(); 56 for (int i = 0; i < n - 1; i++) { 57 int u, v; 58 scanf("%d%d", &u, &v); 59 tree[u].push_back(v); 60 deg[v]++; 61 } 62 int qu, qv; 63 scanf("%d%d", &qu, &qv); 64 que[qu].push_back(qv); 65 que[qv].push_back(qu); 66 for (int i = 1; i <= n; i++) if (deg[i] == 0) { 67 root = i; 68 } 69 int ans; 70 tarjan_LCA(root,ans); 71 printf("%d\n", ans); 72 } 73 return 0; 74 }
2、在线LCA
1 /* 2 author: fenice 3 algorithm: 在线LCA 4 time: 预处理O(nlogn),查询O(logn) 5 */ 6 7 #include<iostream> 8 #include<cstdio> 9 #include<cstring> 10 #include<vector> 11 #include<algorithm> 12 using namespace std; 13 14 const int maxn = 1e4 + 10; 15 16 int n, root; 17 int par[maxn]; 18 int anc[maxn][33]; 19 vector<int> tree[maxn]; 20 21 int hig[maxn];//求深度(根节点为0) 22 void dfs(int cur, int h) { 23 hig[cur] = h; 24 for (int i = 0; i < tree[cur].size(); i++) { 25 dfs(tree[cur][i], h + 1); 26 } 27 } 28 29 //预处理:anc[i][j]表示i节点的第2^j个祖先,它的父亲是它的第2^0个祖先,以此从下往上推。 30 void pre() { 31 for (int i = 0; i < n; i++) anc[i][0] = par[i]; 32 for (int j = 1; j < 32; j++) { 33 for (int i = 0; i < n; i++) { 34 int x = anc[i][j - 1]; 35 if (x != -1) { 36 anc[i][j] = anc[x][j - 1]; 37 } 38 } 39 } 40 } 41 42 //在线LCA: 43 int solve(int p, int q) { 44 if (hig[p] < hig[q]) swap(p, q); 45 int log; 46 for (log = 0; (1 << log) <= hig[p]; log++); log--; 47 48 //把p提到与q同一层,这里是二进制表示的一个应用。 49 //例子:如果x比y大,那么x减去若干个(1<<i)一定可以变成y 50 for (int i = log; i >= 0; i--) { 51 if (anc[p][i] != -1 && hig[anc[p][i]] >= hig[q]) p = anc[p][i]; 52 } 53 54 //这里比较容易漏 55 if (p == q) return p; 56 57 //p,q一起往上提 58 for (int i = log; i >= 0; i--) { 59 if (anc[p][i] != anc[q][i]) { 60 p = anc[p][i]; 61 q = anc[q][i]; 62 } 63 } 64 return par[p]; 65 } 66 67 void init() { 68 memset(par, -1, sizeof(par)); 69 for (int i = 0; i <= n; i++) tree[i].clear(); 70 memset(anc, -1, sizeof(anc)); 71 root = -1; 72 } 73 74 int main() { 75 int tc; 76 scanf("%d", &tc); 77 while (tc--) { 78 scanf("%d", &n); 79 init(); 80 for (int i = 0; i < n - 1; i++) { 81 int u, v; 82 scanf("%d%d", &u, &v); u--; v--; 83 tree[u].push_back(v); 84 par[v] = u; 85 } 86 //找根 87 for (int i = 0; i < n; i++) if (par[i] == -1) { 88 root = i; break; 89 } 90 91 dfs(root, 0); 92 pre(); 93 94 int qu, qv; 95 scanf("%d%d", &qu, &qv); qu--, qv--; 96 printf("%d\n", solve(qu, qv) + 1); 97 } 98 return 0; 99 }
3、树链剖分
#include<map> #include<set> #include<cmath> #include<queue> #include<stack> #include<ctime> #include<vector> #include<cstdio> #include<string> #include<bitset> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<functional> using namespace std; #define X first #define Y second #define mkp make_pair #define lson (o<<1) #define rson ((o<<1)|1) #define mid (l+(r-l)/2) #define sz() size() #define pb(v) push_back(v) #define all(o) (o).begin(),(o).end() #define clr(a,v) memset(a,v,sizeof(a)) #define bug(a) cout<<#a<<" = "<<a<<endl #define rep(i,a,b) for(int i=a;i<(b);i++) #define scf scanf #define prf printf typedef long long LL; typedef vector<int> VI; typedef pair<int,int> PII; typedef vector<pair<int,int> > VPII; const int INF=0x3f3f3f3f; const LL INFL=0x3f3f3f3f3f3f3f3fLL; const double eps=1e-8; const double PI = acos(-1.0); const int maxn = 22222; vector<int> G[maxn]; ///val:边权转点权,fat:父亲,dep:深度,siz:子树节点个数 int fat[maxn], dep[maxn], siz[maxn]; ///son:重儿子,top:链顶,id:dfs序 int son[maxn], top[maxn], id[maxn]; int n; ///求dep,siz; void dfs(int u, int d) { dep[u] = d; siz[u] = 1; int ma = -INF; son[u]=0; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; dfs(v, d + 1); siz[u] += siz[v]; if (ma < siz[v]) { son[u] = v; ma = siz[v]; } } } ///求top,id int _tot; void dfs2(int u,int t) { top[u] = t; id[u] = ++_tot; if (son[u]) dfs2(son[u], t); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (v == son[u]) continue; ///轻链的下端点top等于它自己 dfs2(v,v); } } int solve(int u, int v) { int tu = top[u], tv = top[v]; while (tu != tv) { if (dep[tu] < dep[tv]) { swap(tu, tv); swap(u, v); } u = fat[tu]; tu = top[u]; } return dep[u]>dep[v]?v:u; } void init() { for (int i = 0; i <= n; i++) G[i].clear(); for(int i=0; i<=n; i++) fat[i]=-1; _tot = 0; } int main() { int tc; scf("%d",&tc); while(tc--) { scf("%d",&n); init(); rep(i,0,n-1) { int u,v; scf("%d%d",&u,&v); G[u].pb(v); fat[v]=u; } int rt=0; for(int i=1;i<=n;i++){ if(fat[i]==-1){ rt=i; break; } } dfs(rt,1); dfs2(rt,rt); int qu,qv; scf("%d%d",&qu,&qv); prf("%d\n",solve(qu,qv)); } return 0; }