給出一棵樹。
要求維護:一條路徑上的點權和。
修改1:一條路徑上的點的點權全部加上乙個數。
修改2:shift
假設乙個路徑上的點是ax,ak1,ak2,…ay
就把ax的點權放到ak1上,把ak1的點權放到ak2上,……
把ay的點權放到ax上。
我連去soi的資格都沒有……
當時去聽講,不會splay,不會lct,對映是什麼也聽不懂,處於呆滯狀態。
大佬說是他出的題sone0的子集(%%%)
回來之後,跟著車隊把splay學了。
發現這題樹鏈剖分套splay挺「好」做的。
於是車隊用這個方法紛紛以5000+bytes過了。
但是我沒有打,因為我那會兒沒有時間。
現在我學了lct,於是找了這題來練練手。
lct中,splay把整棵樹分成了若干部分,我們可以把每個splay再對映一顆splay,對映這棵splay維護的就是權值。
這兩個splay的大小一定相同,但是形態不一定相同。
並且如果乙個點x在第一棵splay中的位置是k,那麼它所對應的權值就在第二棵splay的第k個位置。
如果要shift操作,我們只用在第二棵splay中弄就好了。
操作不是重點,重點是我們如何在lct的access和makeroot中使兩顆splay是對應的。
我們可以在每一棵第一棵splay的記錄根記錄這棵splay所對映的那棵splay的根是什麼。
在rotate操作中,維護一下這個東西。
注意在各個操作中,這個東西都可能會發生改變,一定要維護好。
要找到x對映出來的是哪個點,就先把x旋到所在splay的根,對映的splay要維護子樹的大小,然後就可以遞迴找。
code:
#include
#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using
namespace
std;
const
int maxn = 200005;
int dd[maxn], fa[2][maxn], t[2][maxn][2], pf[2][maxn], cas[2][maxn];
struct node a[2][maxn];
int lr(int o, int x)
void fan(int o, int x)
void add(int o, int x, ll y)
void down(int o, int x)
void update(int o, int x)
void rotate(int o, int x)
void xc(int o, int x)
void splay(int o, int x, int y)
}int dfs(int x, int y)
int num(int x)
void access(int x)
}void makeroot(int x)
void link(int x, int y)
int q, n, x, y, z;
char s[10];
int main() else
if(s[1] == 's') else
}}
GDSOI2017 中學生資料結構題
第一行有乙個整數 n,表示 s 國城市的數量。接下來有 n 1 行,每行兩個數 u,v 表示一條道路。第 n 1 行為乙個整數 q,表示接下來有 q 個操作。接下來有 q 行,每行表示乙個操作,格式如題目描述所示。對於每乙個 query 操作,輸出乙個數,表示詢問的當前編號為 x 和編號為 y 的城...
GDSOI2017 中學生資料結構題(LCT)
雖然這只是sone0裡很小的一部分,但是我認為這是最煩的一部分,對著乙個錯誤的題目調了兩天tat 最後才發現自己的程式的輪換打反了 如果想直接用一棵lct來做有乙個最簡單的想法,就是把x到y這段提取出來,然後再輪換一下。但是,這個想法明顯有問題,因為輪換的時候只是換了一下相對順序,並沒有交換權值的大...
中學生資料結構題
給一棵n個帶點權節點 初始為0 的樹 有三種操作 1,對一條路徑上的點的點權全部增加乙個數 2,求一條路徑上的點的點權和 3,對一條路徑進行輪換 假如路徑為a 1 a k則a 1 a 2,a 2 a 3 a k a 1 容易想到用lct維護,輪換操作可以直接把左端點接到右端點的右兒子處,但這樣會改變...