分析:點分治。
首先關於答案的統計轉化成計算每個顏色的貢獻。
1、計算從根出發的路徑的答案:如果某乙個顏色是從根到這個點的鏈上的第一次出現的,那麼這個顏色會對根產生siz[x]個貢獻。(根連向它子樹的任意乙個點的路徑都包含這個顏色)。
2、計算子樹內每個點過根的路徑答案:記錄乙個陣列sum[i],表示從根出發包含顏色i的路徑的條數(在1中,找到乙個第一次出現的顏色,加上這個點的siz即可)。然後假設當前點是x,根為z,x所在的子樹為y。x->z的路徑上,出現的顏色為num,那麼這num個顏色由於已經在到根的路徑上有了,那麼隨便選擇其它子樹內的點構成的路徑都包含了這個顏色,貢獻為(siz[z]-siz[y])*num;未出現的顏色的貢獻:在y子樹外計算多少個點與x構成的路徑,包含這個顏色。那麼可以sum陣列的和得到,但是其中包含的y子樹的路徑,所以一開始要先減掉。
**:
1 #include2 #include3 #include4 #include5 #include6 #include7 #include8 #include9 #include10 #include11
#define fi(s) freopen(s,"r",stdin);
12#define fo(s) freopen(s,"w",stdout);
13using
namespace
std;
14 typedef long
long
ll;15
16 inline int
read()
2021
22const
int inf =1e9;
23const
int n = 100005;24
25int head[n], nxt[n << 1], to[n << 1
], en;
26int
col[n], siz[n], sk[n];
27ll ans[n], cnt[n], sum[n], sum, num;
28bool
vis[n];
29int
size, mn, root, top;
30//
cnt[i] 顏色i出現的次數,sum[i]以根為起點,包含顏色i的路徑條數,sum為sum[i]的和。
3132
void add_edge(int u,int
v) 36
37void getroot(int u,int
fa)
47 cnt = max(cnt, size -siz[u]);
48if (cnt < mn) 49}
5051
void dfs1(int u,int fa)
6061
void dfs2(int u,int fa)
7374
void modify(int u,int fa,int
p) 82
void change(int u,int fa,int
p) 86
87void calc(int
u) 96 num = sum = 0;97
for (int i=1; i<=top; ++i) cnt[sk[i]] = sum[sk[i]] = 0;98
}99void solve(int
u) 108
}109
110int
main()
117 size = n, mn =1e9;
118 getroot(1, 0
);119
solve(root);
120for (int i=1; i<=n; ++i) printf("
%lld\n
",ans[i]);
121return0;
122 }
P2664 樹上遊戲
題面 作為一道經典的點分治題目,此題能很好的考察對點分治的運用。個人認為點分治的本質在於 對於樹上近乎n2 的路徑詢問,通過有效的 劃分,使之能在穩定的時間內通過儲存資訊 獲取資訊的經典方式來求 出答案。由此看出點分治的關鍵在於儲存資訊與獲取資訊的方式。點分治的模板套上之後我們只需要考慮的是子樹與子...
P2664 樹上遊戲 (點分治)
題目鏈結 題面 題解 點分治。考慮當前點分治子樹的根節點為p。那麼我們需要處理兩部分貢獻。所有節點對點p的貢獻 包括p節點自己 經過點p的路徑 x,y 對x點的貢獻。第一類貢獻 如果乙個節點 x 的顏色 color x 在它到根節點 p 的路徑上是第一次出現,那麼以 x 為根的子樹上的每個節點,都可...
洛谷 P2664 樹上遊戲
lrb有一棵樹,樹的每個節點有個顏色。給乙個長度為n的顏色序列,定義 s i,j 為 i 到 j 的顏色數量。以及 sum i sum s i,j 現在他想讓你求出所有的 sum i 這題真是難,點分治神題 我們考慮乙個性質,對於乙個點 i 如果它的顏色在到根的路徑中是第一次出現,那麼對於和 i 不...