luogu2664 樹上遊戲

2022-05-30 20:18:09 字數 1706 閱讀 6830

lrb有一棵樹,樹的每個節點有個顏色。給乙個長度為n的顏色序列,定義s(i,j) 為i 到j 的顏色數量。以及

現在他想讓你求出所有的sum[i]

輸入格式:

第一行為乙個整數n,表示樹節點的數量

第二行為n個整數,分別表示n個節點的顏色c[1],c[2]……c[n]

接下來n-1行,每行為兩個整數x,y,表示x和y之間有一條邊

輸出格式:

輸出n行,第i行為sum[i]

輸入樣例#1:

5

1 2 3 2 3

1 22 3

2 41 5

輸出樣例#1:

10911

912

鏈上資訊,可以考慮點分治。

那麼問題就轉化為了:如何在\(o(n)\)的時間內求出經過\(rt\)的所有鏈資訊,並把答案更新到每個點上。

這樣顯然不是很好做,考慮算每種顏色的貢獻。

對於一種顏色,只有他第一次出現的時候才會造成一點貢獻,可以考慮記個桶來維護顏色的貢獻。

具體的,對於當前的分治塊,對\(rt\)的每個兒子的子樹\(dfs\),如果當前點的顏色是\(rt\)到當前點這條鏈上第一次出現,那麼就把當前點的\(size\)加入桶。

先把所有兒子的子樹全處理完,弄出來乙個桶,注意根的顏色特判。

然後統計答案,列舉根的兒子,先消除當前子樹對桶的貢獻,然後對當前子樹\(dfs\),若當前點顏色第一次出現,就把當前顏色的桶的值改為\(sz[rt]-sz[x]\),\(x\)為當前兒子。

然後記得回溯時還原,每個子樹統計完答案把影響加回來,更改桶的時候同時維護乙個\(sum\)。

細節挺多的,具體看**。

#includeusing namespace std;

#define int long long //偷下懶qaq

void read(int &x)

void print(int x)

void write(int x)

const int maxn = 1e5+10;

const int mod = 1e9+7;

int n,m,col[maxn];

struct input_tree e[maxn<<1];

void add(int u,int v) ,head[u]=tot;}

void ins(int u,int v)

void get_rt(int x,int fa)

f[x]=max(f[x],size-sz[x]);

if(f[x]

}void get_t(int x,int fa,int delta)

void get_ans(int x,int fa)

void clear(int x,int fa)

void solve(int x)

for(int i=head[x];i;i=e[i].nxt)

if(!vis[e[i].to]) size=sz[e[i].to],rt=0,get_rt(e[i].to,x),solve(rt);

}void work()

}t;

signed main()

P2664 樹上遊戲

分析 點分治。首先關於答案的統計轉化成計算每個顏色的貢獻。1 計算從根出發的路徑的答案 如果某乙個顏色是從根到這個點的鏈上的第一次出現的,那麼這個顏色會對根產生siz x 個貢獻。根連向它子樹的任意乙個點的路徑都包含這個顏色 2 計算子樹內每個點過根的路徑答案 記錄乙個陣列sum i 表示從根出發包...

P2664 樹上遊戲

題面 作為一道經典的點分治題目,此題能很好的考察對點分治的運用。個人認為點分治的本質在於 對於樹上近乎n2 的路徑詢問,通過有效的 劃分,使之能在穩定的時間內通過儲存資訊 獲取資訊的經典方式來求 出答案。由此看出點分治的關鍵在於儲存資訊與獲取資訊的方式。點分治的模板套上之後我們只需要考慮的是子樹與子...

Luogu P2664 樹上遊戲

lrb 有一棵樹,樹的每個節點有個顏色。給乙個長度為 n 的顏色序列,定義 s i,j 為 i 到 j 的顏色數量。以及 sum i sum n s i,j 現在他想讓你求出所有的 sum i 第一行為乙個整數 n 表示樹節點的數量 第二行為 n 個整數,分別表示 n 個節點的顏色 c 1 c 2 ...