樹上莫隊演算法

2021-09-07 18:53:13 字數 2859 閱讀 5688

樹上莫隊,顧名思義就是把莫隊搬到樹上。

我們從一道題目入手[sdoi2018]原題識別

spoj count on a tree ii

題目意思很明確:給定乙個$n$個節點的樹,每個節點表示乙個整數,問$u$到$v$的路徑上有多少個不同的整數。

像這種不帶修改數顏色的題首先想到的肯定是樹套樹莫隊,那麼如何把在序列上的莫隊搬到樹上呢?

我們考慮用什麼東西可以把樹上的問題轉化到序列上,dfs序是可以的,但是這道題不行(無法搞lca的貢獻)

有一種神奇的東西,叫做尤拉序。

它的核心思想是:當訪問到點$i$時,加入序列,然後訪問$i$的子樹,當訪問完時,再把$i$加入序列

煮個栗子,下面這棵樹的尤拉序為

有了這個有什麼用呢?

我們考慮我們要解決的問題:求$x$到$y$的路徑上有多少個不同的整數

這裡我們設$st[i]$表示訪問到$i$時加入尤拉序的時間,$ed[i]$表示回溯經過$i$時加入尤拉序的時間

不妨設$st[x]

分情況討論 

若$lca(x,y) = x$,這時$x,y$在一條鏈上,那麼$st[x]$到$st[y]$這段區間中,有的點出現了兩次,有的點沒有出現過,這些點都是對答案沒有貢獻的,我們只需要統計出現過$1$次的點就好

比如當詢問為$2,6$時,$(st[2],st[6])=2\ 3\ 4\ 4\ 5\ 5\ 6$,$4,5$這兩個點都出現了兩次,因此不統計進入答案

若$lca(x,y) \not = x$,此時$x,y$位於不同的子樹內,我們只需要按照上面的方法統計$ed[x]$到$st[y]$這段區間內的點。

比如當詢問為$4,7$時,$(ed[4],st[7]) = 4\ 5\ 5\ 6\ 6\ 3\ 7\ $。大家發現了什麼?沒錯!我們沒有統計$lca$,因此我們需要特判$lca$

然後就沒啦,開始愉快的調**吧

此處純為作者瞎扯。。。

樹上路徑的定義為:從$x$到$y$經過節點個數最少的路徑。

若乙個點$k$出現兩次,說明我們可以先訪問$k$,進入$k$的子樹中,然後出來,再到$y$,很顯然不訪問$k$是更優的。因此出現兩次的點不能統計入答案

從$st[x]$到$ed[x]$為$x$的子樹中的節點,很顯然這些節點不能統計進答案

注意我們詢問的區間長度為$2*n$,所以預處理的時候一定要迴圈到$2*n$!

#include#include

#include

#include

//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?eof:*p1++)

char buf[1

<< 21], *p1 = buf, *p2 =buf;

using

namespace

std;

const

int maxn = 1e5 + 10

;inline

intread()

while(c >= '

0' && c <= '

9') x = x * 10 + c - '

0', c =getchar();

return x *f;

}int

n, q;

intbelong[maxn], block;

struct

query

}q[maxn];

vector

v[maxn];

inta[maxn], date[maxn];

void

discretization()

intdeep[maxn], top[maxn], fa[maxn], siz[maxn], son[maxn], st[maxn], ed[maxn], pot[maxn], tot;

void dfs1(int x, int

_fa)

ed[x] = ++tot; pot[tot] =x;

}void dfs2(int x, int

topfa)

}int getlca(int x, int

y)

return deep[x] < deep[y] ?x : y;

}void

dealask()

}int ans, out

void add(int

x) void delet(int

x) void add(int

x) void

mo()

for(int i = 1; i <= q; i++) out[q[i].id] =q[i].ans;

for(int i = 1; i <= q; i++)

printf(

"%d\n

", out

[i]);

}int

main()

deep[

1] = 1; dfs1(1, 0

); dfs2(

1, 1

);/*

for(int i = 1; i <= n; i++)

for(int j = 1; j <= i - 1; j++)

printf("%d %d %d\n", i, j, getlca(i, j));

*/dealask();

mo();

return0;

}

樹上莫隊演算法

繼續回來寫部落格 記錄點有意思的題目什麼的。貌似寫過這個的沒多少人 所以我也記錄一點。首先序列上的莫隊大家都應該很熟悉了 那麼樹上的莫隊要怎麼搞呢?先來看個題目 spoj cot2 求樹上兩點間路徑上有多少個不同的點權。序列上的莫隊是把詢問按照左端點分塊了 可是樹上沒有左端點,怎麼辦呢?我們把樹分塊...

樹上莫隊演算法

樹上莫隊,顧名思義就是把莫隊搬到樹上。我們從一道題目入手 sdoi2018 原題識別 spoj count on a tree ii 題目意思很明確 給定乙個 n 個節點的樹,每個節點表示乙個整數,問 u 到 v 的路徑上有多少個不同的整數。像這種不帶修改數顏色的題首先想到的肯定是樹套樹莫隊,那麼如...

莫隊演算法學習筆記(三) 樹上莫隊

樹上莫隊的核心思想,就是將一棵樹轉化成乙個序列,然後用普通莫隊來搞。以一棵樹為例 要想對這棵樹進行樹上莫隊,我們第一步就是用乙個 s 陣列把它的括號序存下來 id 12 3456 78910 1112 1314 1516 s 12 4788 7455 2366 31 同時,我們用 i 陣列儲存每個數...