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 ...