本來只是想練練lct,沒想到是個線段樹
對於操作1:誒新的顏色?這不是access嗎?
也就是說,我們用一棵splay來表示一種顏色
操作2直接在lct上亂搞……
不對啊,操作3要查子樹
誒好像是靜態的
那可以考慮線段樹維護dfs序
現在要考慮怎麼維護權值
我們發現開始的時候權值就是節點的深度
而在且只在access的時候會改變權值
試試魔改access?
原來:
for (int y=0;x;y=x,x=fa[x])
那麼主要就是\(ch[x][1]=y\)
實際上這句話包含兩個操作:清除\(ch[x][1]\),把\(y\)接到\(x\)的右兒子
清除\(ch[x][1]\),相當於把原來的重邊斷開
我們只考慮當前這個點改了之後的影響
注意既然他們是重邊連起來的,他們原來一定是同色的
而改了之後他們就不同了,所以y的子樹權值都會增加1
並且他也不會和這條到根路徑上任何乙個出現過的顏色相同
同樣,把\(y\)接到\(x\)的右兒子,意味著x和y同色了,而原來是不同的,所以y的子樹權值都會減少1
線段樹維護一下就好
操作2直接單點查詢,類似樹上字首和就可以了
於是乎,lct40多行……
#include #include #include #include #define maxn 1000005
#define maxm 2000005
using namespace std;
inline int read()
struct edge
e[maxm];
int head[maxn],nxt[maxm],cnt;
void addnode(int u,int v)
; nxt[cnt]=head[u];
head[u]=cnt;
}int dep[maxn],fa[maxn],up[maxn][20];
int dfn[maxn],pos[maxn],tim,end[maxn];
void dfs(int u)
end[u]=tim;
}int lca(int x,int y)
namespace sgt
t[maxn<<2];
void pushup(int p)
void pushlazy(int p,int v)
void pushdown(int p) }
void build(int p,int l,int r)
int mid=(l+r)>>1;
build(lc,l,mid),build(rc,mid+1,r);
pushup(p);
} void modify(int p,int l,int r,int v)
int querymax(int p,int l,int r)
int query(int p,int k) }
using namespace sgt;
namespace splay
int get(int x)
void rotate(int x)
void splay(int x)
rotate(x);
} }}using namespace splay;
namespace lct
void access(int x)
if (ch[x][1]=y)
} }}using namespace lct;
int main()
dep[1]=1;
dfs(1);
build(1,1,n);
while (m--) }
return 0;
}
Sdoi2017 樹點塗色 lct 線段樹
題意 一棵有根樹,支援x到根染成新顏色,求x到y顏色數,求x子樹裡點到根顏色數最大值 考場發現這個資訊是可減的,但是沒想到lct 特意設計成lct的形式!如何求顏色數?維護乙個點和父親的顏色是否一樣,不一樣為1,就是字首和。考慮相鄰的思想和那道 水位線 有點像 x到y的答案就是 s x s y 2 ...
SDOI2017 樹點塗色 LCT 線段樹
可以發現更新操作就是 text 的 text 操作,這個操作複雜度是 o n log n 的 因此,考慮對於每次的 text 操作,維護每個點到根的路徑上不同的權值個數 每次 text 操作只設計到合併兩個鏈 斷開一條鏈兩種操作,可以通過線段樹維護子樹修改 那麼修改的複雜度就是 o n log 2 ...
SDOI2017 樹點塗色(LCT 線段樹)
給你一棵以 1 為根,有 n 個節點的樹,初始時每個節點的顏色互不相同,記一條路徑的權值為這條路徑 包括起點和終點 上所有不同的顏色個數,一共 m 個操作,每次支援以下 3 種操作之一 把點 x 到根節點的路徑上所有的點染上一種沒有用過的新顏色 求 x 到 y 的路徑的權值 在以 x 為根的子樹中選...