Qtree4 動態點分治

2022-02-05 16:13:26 字數 2428 閱讀 7419

給出一棵邊帶權的節點數量為n的樹,初始樹上所有節點都是白色。有兩種操作:

c x,改變節點x的顏色,即白變黑,黑變白

a,詢問樹中最遠的兩個白色節點的距離,這兩個白色節點可以重合(此時距離為0)。

n (n <= 100000) q <= 200000

時限1s

如果沒有修改的話,直接點分治,記錄子樹最深的白點即可。

但是有修改。

發現,點分治的遞迴層數是o(logn)的

而且這個遞迴的聯通塊的根形成乙個樹形結構。不妨叫點分樹。

我們的答案是在所有遞迴出來的塊裡ans取max

發現,每次改變乙個點的顏色,會影響自己的聯通塊,以及點分樹上這個點的所有father的答案。

樹高logn

所以,我們考慮暴力修改每一層的答案。

用三個堆來維護。

乙個堆h0,維護這個點p所代表的點分樹的聯通塊中所有點到點分樹上p的father的距離(樹上實際距離)。

(好處是,修改的時候,直接自底向上,父親只要乙個,這樣不需要在上層再考慮哪個子樹變了)

另乙個堆h1,維護這個點p的所有兒子的堆頂的值。

我們從乙個點p的h1堆裡面,找到最大的和次大的,做和就是這一層的最大答案。

如果p是白點,那麼答案可以只要最大的。而且ans最少是0

第三個堆,維護所有點代表的聯通塊的ans。最終答案就在這裡。

然後,所有的修改,都是刪除之後再插入。

堆的刪除,用懶惰堆即可。

**:(實現細節較多:例如堆的empty判斷)

(堆中不用記錄答案出自**,隨便刪除乙個,剩下那個就是沒有刪除的。是沒有區別的。直接int的堆即可)

//

luogu-judger-enable-o2

#include#define reg register int

#define il inline

#define numb (ch^'0')

using

namespace

std;

typedef

long

long

ll;il

void rd(int &x)

il void prin(int

x)namespace

miraclee[

2*n];

inthd[n],cnt;

il void add(int x,int y,int

z)int

fa[n];

intsz[n];

intc[n];

priority_queue

h[2][n],d[2

][n],hh,dd;

int dis[n][20

];int

dep[n];

intnowsz;

intrt,mxsz[n];

intans[n];

bool

vis[n];

intgen;

il void dfs1(int x,int ff,int

d) mxsz[x]=max(mxsz[x],nowsz-sz[x]);

if(mxsz[x]<=nowsz/2)}

il void dfs2(int x,int ff,intd)}

il void clear(int x,intk)}

il void upda(int

x) clear(x,1);

if(!h[1

][x].empty())

h[1][x].push(tmp);

}}il

int divi(int x,int d,int

ff) }

upda(now);

//cout<<" rt "return

now;

}il

void wrk(int

x)

x=fa[x];

--nd;}}

else

x=fa[x];

--nd;}}

}int

main()

nowsz=n;

gen=divi(1,1,0

);

intm;

rd(m);

char ch[10

];

while(m--)

if(!hh.size())

else}}

else

//cout<<" ans------- "for(reg i=1;i<=n;++i)

}

return0;

}}intmain()

/*author: *miracle*

date: 2018/11/28 9:20:15

*/

你也可以順帶ac[zjoi2007]捉迷藏

QTREE4小結 我寫的是樹剖

為啥說是小結。這辣雞題我寫了兩天。順帶其中參觀了一下scoi的大新聞 這題思路首先是樹剖,剖出來之後,考慮對於每個點維護乙個d1,d2,表示從這個點往下走,不經過自己所在重鏈的邊,到達乙個黑點的最長路徑和次長路徑。如果下面黑點不足就是 inf了。處理出來之後,對於一條鏈,線段樹維護這麼幾個值,lv ...

點分治 動態點分治

實在拖得太久了。先扔掉資料 分治的核心是盡量把乙個整體分成接近的兩個部分,這樣遞迴處理可以讓複雜度從n 變成nlogn。兩個問題,如何區分和如何算答案。對於第乙個問題,重心,然後就是找重心的方法,兩個dfs,對於第二個問題,對於每個重心算當前塊中每個點到重心的答案,然後由重心分開的塊要把多餘的資訊去...

點分治與動態點分治

點分治一般是用於解決樹上路徑問題。樹的重心 把重心這個點割掉後,使所形成的最大的聯通塊大小最小的點。可以證明重心子樹的大小最大不會超過 n over 2 重心可以通過 dfs 一遍求出。maxsiz x 表示割掉點x後所形成的的最大的聯通塊的大小 void dfs int x,int fa max ...