1. 程式人生 > >樹上莫隊演算法

樹上莫隊演算法

江湖傳聞,莫隊演算法能夠解決一切區間查詢問題。這樣說來,莫隊演算法也能夠解決一切樹上路徑查詢問題,將樹上操作轉化為DFS序列上的區間操作即可。當然考慮到,樹上路徑在DFS序列中的性質,還要會求LCA

這裡寫圖片描述

考慮上圖中的樹,其DFS序為

這裡寫圖片描述

其任意點對ab之間的路徑,具有如下性質,令lcaab的最近公共祖先:

  1. lcaab之一,則ab之間的In時刻的區間或者Out時刻區間就是其路徑。例如AK之間的路徑就對應區間ABEEFK,或者KFBCGGHHIICA
  2. lca另有其人,則ab之間的路徑為In[a]Out[b]之間的區間或者In[b]Out[a]之間的區間。另外,還需額外加上l
    ca
    !!!考慮EK路徑,對應為EFK再加上B。考慮EH之間的路徑,對應為EFKKFBCGGH再加上A

這樣就能將路徑查詢轉化為對應的區間查詢。另外需要注意到,在DFS序上應用莫隊演算法移動指標時,如果是欲新增的節點在當前區間內已經有一個了,這實際上應該是一個刪除操作;如果欲刪除的節點在當前區間內已經有兩個了,這實際上應該是一個新增操作。

SPOJ COT2(編號為10707)要求查詢路徑之間不同權值的個數,是一個典型的樹上莫隊題目。

首先求出DFS序,然後弄個數據結構和Tarjan演算法把所有詢問的LCA求出,再根據LCA的結果將原始詢問轉化為區間問題,最後使用莫隊演算法。

#include <cstdio>
#include <vector> #include <algorithm> using namespace std; int const SIZE = 40100; int const BLOCK_SIZE = 300; //利用hash記錄LCA struct Hash{ typedef struct __t{int a;int b;__t(int aa=0,int bb=0):a(aa),b(bb){}} key_t; typedef int value_t; enum{MOD=0x1FFFFF}; key_t keys[MOD+1]; value_t values[MOD+1
]; int head[MOD+1]; int next[MOD+1]; int toUsed; Hash():toUsed(0){fill(head,head+MOD+1,-1);} void clear(){fill(head,head+toUsed,-1);toUsed=0;} int getKey(key_t const&key)const{ int ret = 17; ret = ret * 37 + key.a; ret = ret * 37 + key.b; return ret; } void insert(key_t const&key,value_t const&value){ int k = getKey(key) & MOD; keys[toUsed] = key; values[toUsed] = value; next[toUsed] = head[k]; head[k] = toUsed++; } value_t find(key_t const&key)const{ int k = getKey(key) & MOD; for(int i=head[k];i!=-1;i=next[i]){ if ( keys[i].a == key.a && keys[i].b == key.b ) return values[i]; } return 0; } void disp(FILE*fp)const{ for(int i=1;i<toUsed;++i){ fprintf(fp,"(%d %d): %d\n",keys[i].a,keys[i].b,values[i]); } } }Lca; struct edge_t{ int to; int next; }Edge[SIZE<<1]; int ECnt = 1; int Vertex[SIZE] = {0}; inline void mkEdge(int a,int b){ Edge[ECnt].to = b; Edge[ECnt].next = Vertex[a]; Vertex[a] = ECnt++; Edge[ECnt].to = a; Edge[ECnt].next = Vertex[b]; Vertex[b] = ECnt++; } //生成DFS序 int InIdx[SIZE],OutIdx[SIZE]; int NewIdx[SIZE<<1]; int NCnt = 1; void dfs(int node,int parent){ NewIdx[NCnt] = node; InIdx[node] = NCnt++; for(int next=Vertex[node];next;next=Edge[next].next){ int to = Edge[next].to; if ( to != parent ) dfs(to,node); } NewIdx[NCnt] = node; OutIdx[node] = NCnt++; } //Tarjan演算法中用到的並查集 int Father[SIZE]; int find(int x){return x==Father[x]?x:Father[x]=find(Father[x]);} bool Flag[SIZE] = {false}; vector<vector<int> > Questions(SIZE,vector<int>()); //Tarjan演算法一次性求出所有的LCA void Tarjan(int u,int parent){ Father[u] = u; Flag[u] = true; for(int next=Vertex[u];next;next=Edge[next].next){ int to = Edge[next].to; if ( to == parent ) continue; Tarjan(to,u); Father[to] = u; } vector<int>&vec=Questions[u]; for(vector<int>::iterator it=vec.begin();it!=vec.end();++it){ int v = *it; if ( Flag[v] ){ int r = find(v); Lca.insert(Hash::key_t(u,v),r); Lca.insert(Hash::key_t(v,u),r); } } } struct _t{ int s,e; int idx; int lca; }; bool operator < (_t const&lhs,_t const&rhs){ int ln = lhs.s / BLOCK_SIZE; int rn = rhs.s / BLOCK_SIZE; return ln < rn || ( ln == rn && lhs.e < rhs.e ); } int N,M; int A[SIZE]; _t B[100000]; //將原樹上的路徑問題轉化為DFS序中的區間問題 inline void mkQuestion(int a,int b,int idx){ int lca = Lca.find(Hash::key_t(a,b)); if ( lca == a || lca == b ){ int t = lca == a ? b : a; B[idx].s = OutIdx[t]; B[idx].e = OutIdx[lca]; B[idx].lca = 0; }else{ B[idx].lca = lca; if ( OutIdx[a] < InIdx[b] ) B[idx].s = OutIdx[a], B[idx].e = InIdx[b]; else B[idx].s = OutIdx[b], B[idx].e = InIdx[a]; } } int MoAns; int Ans[100000],Cnt[SIZE]; inline void insert(int n){ if ( 1 == ++Cnt[n] ) ++MoAns; } inline void remove(int n){ if ( 0 == --Cnt[n] ) --MoAns; } void MoOp(int idx){ int k = NewIdx[idx]; if ( Flag[k] ) remove(A[k]); else insert(A[k]); Flag[k] ^= 1; } void Mo(){ sort(B,B+M); fill(Flag,Flag+N+1,false); int curLeft = 1; int curRight = 0; MoAns = 0; for(int i=0;i<M;++i){ while( curRight < B[i].e ) MoOp(++curRight); while( curLeft > B[i].s ) MoOp(--curLeft); while( curRight > B[i].e ) MoOp(curRight--); while( curLeft < B[i].s ) MoOp(curLeft++); if ( B[i].lca ){ Ans[B[i].idx] = MoAns + ( 0 == Cnt[A[B[i].lca]] ? 1 : 0 ); }else{ Ans[B[i].idx] = MoAns; } } } void init(int n){ ECnt = NCnt = 1; fill(Vertex,Vertex+n+1,0); fill(Flag,Flag+n+1,false); } int getUnsigned(){ char ch = getchar(); while( ch > '9' || ch < '0' ) ch = getchar(); int ret = 0; do ret = ret * 10 + (int)(ch-'0');while( '0' <= (ch=getchar()) && ch <= '9' ); return ret; } int W[SIZE]; bool read(){ if ( EOF == scanf("%d",&N) ) return false; M = getUnsigned(); init(N); //權值輸入並離散化 for(int i=1;i<=N;++i) W[i] = A[i] = getUnsigned(); sort(W+1,W+N+1); int* pn = unique(W+1,W+N+1); for(int i=1;i<=N;++i) A[i] = lower_bound(W+1,pn,A[i]) - W; int a,b; for(int i=1;i<N;++i){ a = getUnsigned(); b = getUnsigned(); mkEdge(a,b); } dfs(1,0); for(int i=0;i<M;++i){ B[i].s = getUnsigned(); B[i].e = getUnsigned(); B[i].idx = i; Questions[B[i].s].push_back(B[i].e); Questions[B[i].e].push_back(B[i].s); } Tarjan(1,0); for(int i=0;i<M;++i) mkQuestion(B[i].s,B[i].e,i); return true; } int main(){ //freopen("1.txt","r",stdin); while ( read() ){ Mo(); for(int i=0;i<M;++i)printf("%d\n",Ans[i]); } return 0; }