樹點塗色 SDOI2017

2022-05-27 11:27:14 字數 3896 閱讀 9551

【題目描述】

bob 有一棵 \(n\) 個點的有根樹,其中 \(1\) 號點是根節點。bob 在每個點上塗了顏色,並且每個點上的顏色不同。

定義一條路徑的權值是:這條路徑上的點(包括起點和終點)共有多少種不同的顏色。

bob可能會進行這幾種操作:

bob一共會進行 \(m\) 次操作

【輸入格式】

第一行兩個數 \(n,m\)。

接下來 \(n-1\) 行,每行兩個數 \(a,b\),表示 \(a\) 與 \(b\) 之間有一條邊。

接下來 \(m\) 行,表示操作,格式見題目描述

【輸出格式】

每當出現 \(2,3\) 操作,輸出一行。

如果是 \(2\) 操作,輸出乙個數表示路徑的權值

如果是 \(3\) 操作,輸出乙個數表示權值的最大值

想了5分鐘就決定去看題解的我這種蒟蒻是屑

看了題解就開始寫 居然一次就過了 學資料結構學傻了(確信)

我們先來看一下這個1操作 由於每次都是把到根的路徑染一種全新的顏色 所以任意一種顏色的所有點都一定在連續的一條路徑上面

然後再看這個 每次都是修改從點\(x\)到根的路徑 有沒有想到lct的access操作 access就是將根到點\(x\)的路徑上的點全部扔到乙個splay裡面

所以思路就很顯然了 我們用lct來維護點的顏色 也就是把所有顏色相同的點全部都放在一棵splay裡面

初始時因為所有點顏色各不相同 所以所有邊都是虛邊 也就是說每個點都是一棵splay

然後每次1操作直接access即可

再看看兩種詢問如何回答

先看看2操作 這裡有乙個性質 記\(x\)到根路徑上的顏色種數為f[x],那麼\(x,y\)路徑上的顏色種數為f[x]+f[y]-2*f[lca]+1

一眼看上去很正常 等等+1是什麼鬼

\(lca\)那個點的顏色假設是\(c\) 那麼\(c\)這種顏色在f[x]f[y]被各加了一次 又在-2*f[lca]被減了兩次 所以還要加回來

問題是f[x]在access時要怎麼維護

從oi-wiki盜了張圖 _(:з」∠)_

有一棵長成這樣的樹

假設現在它的輕重邊劃分是這樣的

(注意 這棵是原樹 不是lct)

現在我們把\(a\rightarrow n\)的點全部染成一種新的顏色

邊的虛實要這樣修改

那麼\(n\rightarrow o\)這條邊就會從實邊變成虛邊 因為\(n\)的顏色不再和\(o\)一樣了

所以我們把\(o\)子樹中所有點的f[x]+1

再往上走的時候\(i\rightarrow k\)也從實邊變成虛邊 一樣把\(k\)子樹中所有點f[x]+1

然後我們發現\(i\rightarrow l\)這條邊由虛變實了 也就是說原來\(i\)和\(l\)的顏色不一樣 現在一樣了 所以把\(l\)子樹中所有點f[x]-1

總而言之 就是虛變實要-1實變虛要+1

在access裡面實現的時候就是每次找到當前這棵splay裡面最靠左的點(按定義這個點是原樹中深度最淺的點) 把這個點的子樹+1-1

不知道如何解釋。。。具體見**

然後就好辦了 子樹區間加用線段樹就完了

2操作就是找\(lca\)然後線段樹單點查詢 直接輸出即可

3操作就是線段樹區間取\(\text\)

初始時由於每個點的顏色互不相同 所以f[x]就等於\(x\)在原樹上的深度(也就是到根的路徑上有多少個點)

碼量極大 我寫了200行。。。(雖然一遍就過了)

看網上有人說這題是lct+樹剖 但是實際上沒有用到樹剖的思想。。。頂多用了一下求lca

但是似乎也有純樹剖切此題的神犇orz

#include #define n 100005

using namespace std;

inline int read()

int n, m, head[n], pre[n<<1], to[n<<1], sz;

inline void addedge(int u, int v)

int fa[n], dfn[n], ed[n], rnk[n], tme, d[n], top[n], son[n], siz[n];

//由於還要維護什麼深度 dfs序之類的東西 所以直接來一次樹剖比較方便

void dfs(int x)

}void dfs2(int x, int tp)

ed[x] = tme;

}inline int lca(int x, int y)

if (d[x] > d[y]) swap(x, y);

return x;

}namespace segtree tr[n<<2];

#define lson ind<<1

#define rson ind<<1|1

void build(int ind, int l, int r)

int mid = (l + r) >> 1;

build(lson, l, mid); build(rson, mid+1, r);

tr[ind].mx = max(tr[lson].mx, tr[rson].mx); }

inline void pushdown(int ind)

void update(int ind, int x, int y, int v)

pushdown(ind);

int mid = (l + r) >> 1;

if (x <= mid) update(lson, x, y, v);

if (mid < y) update(rson, x, y, v);

tr[ind].mx = max(tr[lson].mx, tr[rson].mx); }

int query(int ind, int x, int y)

#undef lson

#undef rson

}namespace lct

inline void pushdown(int x)

inline bool isroot(int x)

inline void rotate(int x)

int q[n], top;

inline void splay(int x) }

inline int pre(int x)

inline void access(int x)

ch[x][1] = i;

if (ch[x][1])

} }#undef lson

#undef rson

}int main()

d[1] = 1;

dfs(1); dfs2(1, 1);

segtree::build(1, 1, n);

for (int i = 1; i <= n; i++)

for (int i = 1, tp, x, y; i <= m; i++) else if (tp == 2) else

} return 0;

}

SDOI2017 樹點塗色

description bob有一棵n個點的有根樹,其中1號點是根節點。bob在每個點上塗了顏色,並且每個點上的顏色不同。定義一條路 徑的權值是 這條路徑上的點 包括起點和終點 共有多少種不同的顏色。bob可能會進行這幾種操作 1 x 把點x到根節點的路徑上所有的點染上一種沒有用過的新顏色。2 x ...

SDOI2017 樹點塗色

傳送門 塗色的操作和acc es saccess access 很像啊,如何用lct lctlc t維護這個東西呢。由於每次覆蓋的顏色都不同,且是從當前到結點覆蓋到根節點。那麼如果把顏色相同的一段維護在一條重鏈上,乙個點到根要經過多少虛邊也就包含多少顏色。所以用lct lctlc t模擬覆蓋的過程,...

SDOI2017 樹點塗色

題目鏈結 有三種操作,1.把點 x xx 到根節點的路徑上所有的點染上一種沒有用過的新顏色。2.求 x xx 到 y yy 的路徑的上的不同顏色數。3.在以x xx為根的子樹中選擇乙個點,使得這個點到根節點的路徑的不同顏色數最大,求最大權值。可以發現修改操作的乙個性質 每種顏色的節點一定會形成一條鏈...