樹上莫隊,顧名思義就是把莫隊搬到樹上。
我們從一道題目入手[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 陣列儲存每個數...