樹上莫隊演算法
阿新 • • 發佈:2019-02-02
江湖傳聞,莫隊演算法能夠解決一切區間查詢問題。這樣說來,莫隊演算法也能夠解決一切樹上路徑查詢問題,將樹上操作轉化為DFS序列上的區間操作即可。當然考慮到,樹上路徑在DFS序列中的性質,還要會求
考慮上圖中的樹,其DFS序為
其任意點對
- 若
lca 是a 、b 之一,則a 、b 之間的In 時刻的區間或者Out 時刻區間就是其路徑。例如AK 之間的路徑就對應區間ABEEFK ,或者KFBCGGHHIICA 。 - 若
lca 另有其人,則a 、b 之間的路徑為In[a] 、Out[b] 之間的區間或者In[b] 、Out[a] 之間的區間。另外,還需額外加上l !!!考慮EK 路徑,對應為EFK 再加上B 。考慮EH 之間的路徑,對應為EFKKFBCGGH 再加上A 。
這樣就能將路徑查詢轉化為對應的區間查詢。另外需要注意到,在DFS序上應用莫隊演算法移動指標時,如果是欲新增的節點在當前區間內已經有一個了,這實際上應該是一個刪除操作;如果欲刪除的節點在當前區間內已經有兩個了,這實際上應該是一個新增操作。
SPOJ COT2(編號為10707)要求查詢路徑之間不同權值的個數,是一個典型的樹上莫隊題目。
首先求出DFS序,然後弄個數據結構和Tarjan演算法把所有詢問的
#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;
}