Luogu P3806 模板 點分治1

2021-09-25 20:17:04 字數 3313 閱讀 2812

%

給定一棵有 n

nn 個點,邊權的樹,回答 m

mm 個詢問,每次詢問樹上距離為 k

kk 的點對是否存在。

資料範圍n⩽1

04,m

⩽100,邊權

⩽10000,k

⩽107

n\leqslant 10^4,m\leqslant 100,\texttt\leqslant 10000,k\leqslant 10^7

n⩽104,

m⩽10

0,邊權⩽

1000

0,k⩽

107%

點分治模板。

考慮以 u

uu 為根的子樹,這棵子樹上的路徑有兩種,一種是經過節點 u

uu 的,另一種是不經過根節點的,考慮分治。對於大小為 1

11 的子樹,無樹上路徑,直接返回。對於節點 u

uu,合併不同的兩個子樹的路徑,組成經過節點 u

uu 的一條新路徑。

對於層數為 k

kk 的樹,時間複雜度為 θ(k

log⁡2n

)\theta(k\log_2n)

θ(klog2​

n),在平均情況下良好,但當樹為一條鏈時,程式的時間複雜度將為 θ(n

2)

\theta(n^2)

θ(n2)。

對於一棵樹 n

nn 個節點的無根樹,找到乙個點,使得把樹變成以該點為根的有根樹時,最大子樹的結點數最小,那這個點就是樹的重心

求解重心可以用dfs求出所有子樹的大小,然後動態規劃求解,時間複雜度為 θ(n

)\theta(n)

θ(n)

。如果我們選取重心作為根,然後統計從根節點出發的所有路徑(θ(n

)\theta(n)

θ(n)

),合併從根節點出發的路徑,得到所有經過根節點的路徑,接著刪除根節點,對剩下的每個子樹分別找重心,重複以上操作,便可求出所有路徑。換句話說:每個點都是根節點,但所管理的子樹大小不同,而且重心的性質保證了最壞情況下劃分的子樹總數和 θ

(log⁡2

n)

\theta(\log_2 n)

θ(log2​n

) 同階。

在合併路徑時,先將所有路徑按長度排序先列舉詢問(θ(m

)\theta(m)

θ(m)

),令當前詢問長度為 k

kk,再列舉每條路徑(θ(n

)\theta(n)

θ(n)

),令路徑長度為 w

ww,最後在路徑中二分查詢長度為 k−w

k-wk−

w 的路徑(θ

(log⁡2

n)

\theta(\log_2n)

θ(log2​n

)),注意此時兩條路徑不能屬於同乙個子樹,因而合併路徑的時間複雜度為 θ(m

nlog⁡2

n)

\theta(mn\log_2n)

θ(mn

log2​n

)。因而程式的總時間複雜度為t(n

)=θ(

mn

log⁡22

n)

\text(n)=\theta(mn\log_2^2 n)

t(n)=θ

(mnlog22

​n)  **如下

#include

#define n 10010

using

namespace std;

int n,m,num,e,x,y,z,a[

110]

,rt,size,siz[n]

,f[n]

,head[n]

;bool ans[

110]

,vis[n]

;struct edgeedges[n<<1]

;struct path

} dis[n]

;template

<

typename t>

void

maxx

(t& a,

const t &b)

void

add(

int x,

int y,

int z)

; head[x]

=e;}

void

getroot

(int x,

int fa)

maxx

(f[x]

,size-siz[x]);

if(f[x]

) rt=x;

}void

dfs(

int x,

int fa,

int wh,

int d)

;for

(int i=head[x]

;i;i=edges[i]

.pre)

}void

work

(int x)

dis[

++num]

=(path)

;sort

(dis+

1,dis+num+1)

;//o(nlogn)

for(

int i=

1;i<=m;

++i)

)-dis;

while

(l<=num&&dis[pot]

.dis+dis[l]

.dis==a[i]

&&dis[pot]

.w==dis[l]

.w) pot++;if

(dis[pot]

.dis+dis[l]

.dis==a[i]

) ans[i]=1

; l++;}

}}void

solve

(int x)

}int

main()

for(

int i=

1;i<=m;

++i)

scanf

("%d"

,a+i)

; f[rt]

=size=n;

getroot(1

,0);

solve

(rt)

;for

(int i=

1;i<=m;

++i)

puts

(ans[i]

?"aye"

:"nay");

}

luogu P3806 模板 點分治1

題目描述 給定一棵有n個點的樹 詢問樹上距離為k的點對是否存在。多次詢問 可離線 我們先隨意指定乙個虛擬根root,將這棵樹轉化成無根樹 樹上的路徑可以分為兩類,1.經過根節點u的路徑 2.完全在u子樹裡 不經過u 的 對於1,用dis表示當前結點到根節點root的路徑長度,則root的子樹中兩個節...

luogu P3806 模板 點分治1

給你一棵樹,路徑有長度,多次詢問,每次給出 k,問你是否存在路徑長度為 k 的點對。這道題我們用分治的方法。那我們假設要解決乙個樹,那我們先選重心作為根節點 為了減少高度節省時間 然後就分成兩種討論,一種是路徑都在同乙個子樹中,那這個我們就可以把問題轉換為解決這個子樹。另一種就是在兩個不同的子樹中 ...

luogu P3806 模板 點分治1

給你一棵樹,路徑有長度,多次詢問,每次給出 k,問你是否存在路徑長度為 k 的點對。這道題我們用分治的方法。那我們假設要解決乙個樹,那我們先選重心作為根節點 為了減少高度節省時間 然後就分成兩種討論,一種是路徑都在同乙個子樹中,那這個我們就可以把問題轉換為解決這個子樹。另一種就是在兩個不同的子樹中 ...