日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[luogu-P4299] 首都(并查集 + LCT动态维护树的重心 / 维护虚儿子信息)

發布時間:2023/12/3 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [luogu-P4299] 首都(并查集 + LCT动态维护树的重心 / 维护虚儿子信息) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

problem

luogu-P4299

solution

本題考察了很經典的模型,運用了很經典的解法。

本題用到了重心的兩個性質:

  • 兩棵樹合并為同一棵樹時,新的重心一定在原來兩棵樹各自重心的路徑上。

  • 重心為根時的最大子樹大小最小,不超過 siz/2siz/2siz/2

    如果樹節點個數為奇數,則只有一個重心。否則有兩個重心。

找到 x,yx,yx,y 各自所在的國家的重心 gx,gygx,gygx,gy

x,yx,yx,y 連邊完成國家合并。

然后把 gx?gygx-gygx?gy 的路徑 split\text{split}split 出來。

接著就暴力 dfs\text{dfs}dfs 根據左右兒子的 siz\text{siz}siz 判斷走左還是走右,因為每次 sizsizsiz 至少減小一半,所以時間復雜度仍然是一個 logloglog

具體而言:當左右子樹 siz\text{siz}siz 都不超過總個數的一半時,將這個點納入重心備選。然后看左右兒子誰的 sizsizsiz 更大,就往哪邊走。

但是很遺憾 LCT\text{LCT}LCT 認父不認子,對于一個點只能記錄兩個左右兒子的 siz\text{siz}siz

我們好像不能獲得一個點的子樹內所有的節點個數?

這就是一個 LCT\text{LCT}LCT 經典應用了。

t[i].sizt[i].sizt[i].siz 記錄 iii 子樹大小,t[i].tott[i].tott[i].tot 記錄除開兩個左右兒子的 sizsizsiz,其余虛兒子的 sizsizsiz 的和。

更新就需要寫成:

t[x].siz = t[t[x].son[0]].siz + t[t[x].son[1]].siz + 1 + t[x].tot;

與此同時,所有涉及更改虛實兒子的,都要重新維護 tottottot

一般就是 access\text{access}access 中的操作,會多加一行。

t[x].tot += t[t[x].son[1]].siz - t[son].siz;

而且為了保證信息的時效性,我們的 link\text{link}link 操作要還是像以前一樣,直接記一下 xxx 的父親是 yyy 就不行了。

也不能直接把 yyy 的信息更新一下就完了。

因為一旦 x,yx,yx,y 連邊,各自所在的子樹的位置一路往上信息都要修改。

所以我們不如直接讓 x,yx,yx,y 成為聯通塊的根,split(x,y)\text{split(x,y)}split(x,y) 即可。

由于 LCTLCTLCTfindroot\text{findroot}findroot 慢得慌,我們直接用并查集維護每個聯通塊的重心即可,重心的異或和也用個變量維護即可。

具體可見代碼實現。

code

#include <bits/stdc++.h> using namespace std; #define maxn 100005namespace LCT {int top = 0, sta[maxn];struct node { int son[2], fa, tot, tag, siz; }t[maxn];bool root( int x ) { return t[t[x].fa].son[0] ^ x and t[t[x].fa].son[1] ^ x; }void pushup( int x ) { t[x].siz = t[t[x].son[0]].siz + t[t[x].son[1]].siz + 1 + t[x].tot; }void reverse( int x ) { swap( t[x].son[0], t[x].son[1] ); t[x].tag ^= 1; }void pushdown( int x ) {if( ! t[x].tag ) return;if( t[x].son[0] ) reverse( t[x].son[0] );if( t[x].son[1] ) reverse( t[x].son[1] );t[x].tag = 0;}void rotate( int x ) {int fa = t[x].fa;int Gfa = t[fa].fa;int d = t[fa].son[1] == x;if( ! root( fa ) ) t[Gfa].son[t[Gfa].son[1] == fa] = x;t[x].fa = Gfa;if( t[x].son[d ^ 1] ) t[t[x].son[d ^ 1]].fa = fa;t[fa].son[d] = t[x].son[d ^ 1];t[x].son[d ^ 1] = fa;t[fa].fa = x;pushup( fa );pushup( x );}void splay( int x ) {sta[++ top] = x; int y = x;while( ! root( y ) ) sta[++ top] = y = t[y].fa;while( top ) pushdown( sta[top --] );while( ! root( x ) ) {int fa = t[x].fa, Gfa = t[fa].fa;if( ! root( fa ) ) (t[Gfa].son[1] == fa) ^ (t[fa].son[1] == x) ? rotate( x ) : rotate( fa );rotate( x ); }}void access( int x ) { for( int son = 0;x;son = x, x = t[x].fa ) { splay( x );t[x].tot += t[t[x].son[1]].siz - t[son].siz;t[x].son[1] = son;pushup( x ); }}void makeroot( int x ) { access( x ); splay( x ); reverse( x ); }void split( int x, int y ) { makeroot( x ); access( y ); splay( y ); }void link( int x, int y ) { split( x, y ); t[t[x].fa = y].tot += t[x].siz; pushup( y ); }int dfs( int x ) {int lsum = 0, rsum = 0, siz = t[x].siz >> 1, flag = t[x].siz & 1, ans = 1e9;while( x ) {pushdown( x );int lcnt = lsum + t[t[x].son[0]].siz;int rcnt = rsum + t[t[x].son[1]].siz;if( lcnt <= siz and rcnt <= siz ) {if( flag ) { ans = x; break; }else ans = min( ans, x );}if( lcnt < rcnt ) { lsum += t[x].siz - t[t[x].son[1]].siz; // + t[t[x].son[0]].siz + t[x].tot + 1x = t[x].son[1];}else {rsum += t[x].siz - t[t[x].son[0]].siz; // +t[t[x].son[1]].siz + t[x].tot + 1x = t[x].son[0];}}splay( ans );return ans;} }int f[maxn]; int find( int x ) { return x == f[x] ? x : f[x] = find( f[x] ); }int main() {int ans = 0, x, y, n, m; char opt[10];scanf( "%d %d", &n, &m );iota( f + 1, f + n + 1, 1 );for( int i = 1;i <= n;i ++ ) ans ^= i;for( int i = 1;i <= m;i ++ ) {scanf( "%s", opt );switch( opt[0] ) {case 'X' : printf( "%d\n", ans ); break;case 'Q' : {scanf( "%d", &x );printf( "%d\n", find( x ) );break;}case 'A' : {scanf( "%d %d", &x, &y );LCT :: link( x, y );x = find( x ), y = find( y );LCT :: split( x, y );int g = LCT :: dfs( y );ans ^= x ^ y ^ g;f[x] = f[y] = f[g] = g;break;}}}return 0; }

總結

以上是生活随笔為你收集整理的[luogu-P4299] 首都(并查集 + LCT动态维护树的重心 / 维护虚儿子信息)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。