【uoj#207】共价大爷游长沙 随机化+LCT维护子树信息
題目描述
給出一棵樹和一個點對集合S,多次改變這棵樹的形態、在集合中加入或刪除點對,或詢問集合內的每組點對之間的路徑是否都經過某條給定邊。
輸入
輸入的第一行包含一個整數 id,表示測試數據編號,如第一組數據的id=1,樣例數據的 id 可以忽略。
輸入的第二行包含兩個整數 n,m,分別表示圖中的點數,以及接下來會發生的事件數,事件的定義下文中會有描述。初始時 S 為空。
接下來 n?1 行,每行兩個正整數 x,y,表示點 x 和點 y 之間有一條無向邊。
接下來 m 行,每行描述一個事件,每行的第一個數 type 表示事件的類型。
若type=1,那么接下來有四個正整數x,y,u,v,表示先刪除連接點x和點y的無向邊,保證存在這樣的無向邊,然后加入一條連接點u和點v的無向邊,保證操作后的圖仍然滿足題中所述條件。
若type=2,那么接下來有兩個正整數 x,y,表示在 S 中加入點對 (x,y)。
若type=3,那么接下來有一個正整數 x,表示刪除第 x 個加入 S 中的點對,即在第 x 個 type=2 的事件中加入 S 中的點對,保證這個點對存在且仍然在 S 中。
若 type=4,那么接下來有兩個正整數 x,y,表示小L詢問守在連接點 x 和點 y 的邊上是否一定能見到共價大爺,保證存在這樣的無向邊且此時 S 不為空。
輸出
對于每個小L的詢問,輸出“YES”或者“NO”(均不含引號)表示小L一定能或者不一定能見到共價大爺。
樣例輸入
0
5 7
1 2
1 3
2 4
1 5
2 1 5
1 1 5 2 5
4 2 5
2 1 4
4 2 5
3 1
4 2 4
題解
隨機化+LCT維護子樹信息
對與每個點對,隨機一個權值,把這個權值異或到這兩個點上。那么對于查詢,如果 x 為樹根時,y 子樹中的所有點的權值的異或和等于所有點對的異或和,則視為所有點對間的路徑都經過 x-y 。(別問我怎么想出來的。。。做過一道類似的題)
當權值范圍足夠大時可以近似視為正確。
由于樹的形態是變化的,因此需要使用LCT維護子樹信息,具體方法參見這里。
注意維護子樹信息的LCT:link時需要makeroot(x),makeroot(y);修改時需要makeroot(x)而不是簡單的splay(x);查詢時需要先makeroot(x)。
時間復雜度 $O(LCT·n\log n)$?
#include <cstdio> #include <algorithm> #define N 100010 using namespace std; int fa[N] , c[2][N] , rev[N] , w[N] , sum[N] , vx[N * 3] , vy[N * 3] , vw[N * 3] , tot; inline void pushup(int x) {sum[x] = sum[c[0][x]] ^ sum[c[1][x]] ^ w[x]; } inline void pushdown(int x) {if(rev[x]){int l = c[0][x] , r = c[1][x];swap(c[0][l] , c[1][l]) , rev[l] ^= 1;swap(c[0][r] , c[1][r]) , rev[r] ^= 1;rev[x] = 0;} } inline bool isroot(int x) {return c[0][fa[x]] != x && c[1][fa[x]] != x; } void update(int x) {if(!isroot(x)) update(fa[x]);pushdown(x); } inline void rotate(int x) {int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1;if(!isroot(y)) c[c[1][z] == y][z] = x;fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y;pushup(y) , pushup(x); } inline void splay(int x) {int y , z;update(x);while(!isroot(x)){y = fa[x] , z = fa[y];if(!isroot(y)){if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x);else rotate(y);}rotate(x);} } inline void access(int x) {int t = 0;while(x) splay(x) , w[x] ^= sum[c[1][x]] ^ sum[t] , c[1][x] = t , t = x , x = fa[x]; } inline void makeroot(int x) {access(x) , splay(x) , swap(c[0][x] , c[1][x]) , rev[x] ^= 1; } inline void link(int x , int y) {makeroot(x) , makeroot(y) , fa[x] = y , w[y] ^= sum[x] , pushup(y); } inline void cut(int x , int y) {makeroot(x) , access(y) , splay(y) , fa[x] = c[0][y] = 0 , pushup(y); } int main() {srand(20011011);int n , m , i , opt , x , y , u , v , now = 0;scanf("%*d%d%d" , &n , &m);for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , link(x , y);while(m -- ){scanf("%d%d" , &opt , &x);if(opt == 1) scanf("%d%d%d" , &y , &u , &v) , cut(x , y) , link(u , v);else if(opt == 2){scanf("%d" , &y);vx[++tot] = x , vy[tot] = y , vw[tot] = (rand() << 15) + rand() , now ^= vw[tot];makeroot(x) , w[x] ^= vw[tot] , pushup(x);makeroot(y) , w[y] ^= vw[tot] , pushup(y);}else if(opt == 3){now ^= vw[x];makeroot(vx[x]) , w[vx[x]] ^= vw[x] , pushup(vx[x]);makeroot(vy[x]) , w[vy[x]] ^= vw[x] , pushup(vy[x]);}else scanf("%d" , &y) , makeroot(x) , access(y) , splay(y) , puts(sum[x] == now ? "YES" : "NO");}return 0; }?
轉載于:https://www.cnblogs.com/GXZlegend/p/8244009.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【uoj#207】共价大爷游长沙 随机化+LCT维护子树信息的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cfe刷机教程 斐讯k3_斐讯K3刷机教
- 下一篇: js打开新窗口与页面跳转