RMQ Algorithms
Algorithm | Time Complexity (pre-processing, per-query) | Memory Complexity | Compatibility with dynamic data |
---|---|---|---|
SQRT(N) Segment Algorithm | O(N), O(sqrt(N)) | O(N) | Compatible |
Sparse Table | O(NlogN), O(1) | O(NlogN) | Not compatible |
Segment Tree | O(N), O(logN) | O(N) | Compatible |
LCA Algorithms
Algorithm | Time Complexity (pre-processing, per-query) | Memory Complexity |
---|---|---|
SQRT(H) Segment Algorithm | O(N), O(sqrt(N)) | O(N) |
Dynamic programming (similar to Sparse Table) | O(NlogN), O(logN) | O(NlogN) |
LCA problems can be converted to restricted RMQ problems where consecutive elements differ by at most 1 in O(N) time. RMQ problems can also be converted to LCA problem using Cartesian tree in O(N) time. So, in turn, we can convert any RMQ problem to a restricted RMQ problem by first converting it to LCA and then to RMQ again. Restricted RMQ can be solved using a <O(N), O(1)> algorithm.
This problem's link was given at the end of the tutorial as practice problem.
In this problem
you are given a weighted tree with N nodes (N<=10000) followed by some queries. Queries can be of 2 types - DIST and KTH. In a DIST query, you have to calculate the distance between 2 nodes and in a KTH query, we have to print the k-th node on the path from a particular node to another.Firstly, I have selected a random node as the root of the tree and then ran a DFS to calculate distance to each node from the root. Now, distance between 2 nodes can be easily calculated if we know their LCA.
Dist(a,b) = Dist(root, a) + Dist(root,b) - 2 * Dist(root, LCA)I've used the dynamic programming solution similar to Sparse Table for finding the LCA. In this algorithm, we store the 2^j-th ancestor of each node i. Read the tutorial for details.
Now, when finding the k-th node on the path from a to b, we need to first determine on which segment this k-th node appears
- a to LCA
- LCA to b
If it is on the 2nd segment, we can just swap the nodes and change the value of k accordingly. Then we're left with finding k-th ancestor of a particular node. This can be done utilizing the ancestor array that we pre-computed.
C++ source code below -
/* Problem Name : QTREE2 Problem Number : SPOJ 913 Link : https://www.spoj.pl/problems/QTREE2/ Problem Type : Search, LCA Difficulty : 7 / 10 Interest : 9 / 10 Complexity : O(NlogN), O(logN) Date : December 10, 2010 */ #include <stdio.h> #include <vector> #include <time.h> #include <string> #include <assert.h> #include <algorithm> using namespace std; #define rep(i,n) for(i=0;i<(n);i++) #define MAXN 10000 #define MAXLOGN 14 //#define i64 __int64 #define i64 long long vector< pair<int,int> > adj[MAXN+1]; int n; i64 d[MAXN+1]; char flag[MAXN+1]; int root; int L[MAXN+1]; int father[MAXN+1]; int A[MAXN+1][MAXLOGN+1]; i64 INF = 10000000; void dfs(int cur) { flag[cur] = true; int i; rep(i,adj[cur].size()) { if(!flag[adj[cur][i].first]) { d[adj[cur][i].first] = d[cur] + adj[cur][i].second; father[adj[cur][i].first] = cur; L[adj[cur][i].first] = L[cur] + 1; dfs(adj[cur][i].first); } } } vector<string> parse(const string& s,const string& delim=" ") { vector<string>res; string t; for(int i=0;i!=s.size();i++) { if(delim.find(s[i]) != string::npos) { if(!t.empty()) { res.push_back(t); t=""; } } else t+=s[i]; } if(!t.empty()) res.push_back(t); return res; } void pre_process() { int i,j; for(i=1;i<=n;i++) for(j=0;(1<<j)<=n;j++) A[i][j] = -1; for(i=1;i<=n;i++) A[i][0] = father[i]; for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++) if(A[i][j-1] != -1) A[i][j] = A[A[i][j-1]][j-1]; } int getLCA(int p, int q) { int i,log; if(L[p] < L[q]) swap(p,q); for(log=1;(1<<log)<=L[p];log++); log--; for(i=log;i>=0;i--) if(L[p] - (1<<i) >= L[q]) p = A[p][i]; if(p == q) return p; for(i=log;i>=0;i--) { if(A[p][i] != -1 && A[p][i] != A[q][i]) { p = A[p][i]; q = A[q][i]; } } return father[p]; } int getKth(int p, int q, int k, int LCA) { int d,log,i; if(LCA == p) { d = L[q] - L[p] + 1; swap(p,q); k = d - k + 1; } else if(LCA == q) ; else { if(L[p] - L[LCA] + 1 < k) { d = L[p] + L[q] - 2 * L[LCA] + 1; k = d - k + 1; swap(p,q); } } for(log=1;(1<<log)<=L[p];log++); log--; k--; for(i=log;i>=0;i--) if((1<<i) <= k) { p = A[p][i]; k -= (1<<i); } return p; } int main() { int T; int i,a,b,c,k; i64 res; char s[100]; int LCA; INF *= INF; vector<string> vs; scanf(" %d",&T); srand(time(NULL)); while(T--) { scanf(" %d",&n); for(i=1;i<=n;i++) adj[i].clear(), d[i] = INF, flag[i] = false; for(i=1;i<n;i++) { scanf(" %d %d %d",&a,&b,&c); adj[a].push_back(make_pair(b,c)); adj[b].push_back(make_pair(a,c)); } root = rand() % n + 1; d[root] = 0; father[root] = -1; L[root] = 0; dfs(root); pre_process(); gets(s); while(gets(s)) { if(s[1] == 'O') break; vs = parse(s); a = atoi(vs[1].c_str()); b = atoi(vs[2].c_str()); LCA = getLCA(a,b); assert(LCA >= 1 && LCA <= n); if(vs[0] == "DIST") { if(LCA == a || LCA == b) res = abs(d[a] - d[b]); else res = d[a] + d[b] - 2 * d[LCA]; } else if(vs[0] == "KTH") { k = atoi(vs[3].c_str()); res = getKth(a,b,k,LCA); } else assert(0); printf("%lld\n",res); } printf("\n"); } return 0; }
Nice one Sanny Vai. I have been solving spoj for last few months and there are lots of cool data structure problems.
ReplyDeleteAnother question, what is the syntax highlighter u r using?
I'm using this as syntax highlighter -
ReplyDeletehttp://code.google.com/p/google-code-prettify/
hey i tried to run it......
ReplyDeleteit is giving me segmentation fault
:(
Is the seg fault caused by one of the asserts?
ReplyDeleteQTREE2 te wa ar tle khete khete mara jachilam. Apnar code dekhe bug gula dur korte pere AC pelam. Thanks vaia :)
ReplyDeleteMy solution: runs in 0.66s. Still some optimizations are possible, but this is quite a simple solution http://codepad.org/FaijG9qR
ReplyDeletesource code how to highlight
ReplyDeletehttp://code.google.com/p/google-code-prettify/
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteokay i am new to this kind of data structure
ReplyDeletecan anyone explain if Dist(a,b) = Dist(a, LCA(a,b)) + Dist(b,LAC(a,b))
will work or not for this problem
thanks in advance :)
more-over why use Sparse Table for O(sqrt(n)) when segment tree
ReplyDeletecan do the job in O(log(n))
Sir,can you please do some commenting :)
ReplyDeleteatleast in the kthnode function
ReplyDeleteTLE with python..
ReplyDelete