lrb有一棵樹,樹的每個節點有個顏色。給乙個長度為n的顏色序列,定義\(s(i,j)\)為\(i\)到\(j\)的顏色數量。以及
\[sum_i = \sum_^s(i,j)
\]現在他想讓你求出所有的\(sum[i]\)
這題真是難,點分治神題
我們考慮乙個性質,對於乙個點\(i\),如果它的顏色在到根的路徑中是第一次出現,那麼對於和\(i\)不在乙個子樹的點\(j\),對\(j\)都有\(i\)的子樹大小\(size_i\)的貢獻
然後有了這個性質,就好做了
找完重心後預處理出來實際的\(size\),用\(sum\)來記錄所有點的貢獻,\(s\)是這個顏色的貢獻
而我們不是用點去更新答案,是用顏色來更新答案,所以要枚舉子樹,去掉這個子樹的貢獻來統計答案
於是再有\(x\)表示除了這個子樹的點數和,\(co\)表示這個點到根的顏色數
然後記錄下這個點到根的所有顏色的\(s\)的和,\(s\)是要被減去的
那麼\(ans+=sum-s+co\times x\),然後單獨更新一下根就是\(ans+=sum-s_}+size_\)
code
#include #include #include #include const int n = 1e5;
using namespace std;
int n,c[n + 5],size[n + 5],maxp[n + 5],rt,su,vis[n + 5],cnt[n + 5];
long long sum,s[n + 5],ros,x,ans[n + 5];
vector d[n + 5];
void get_rt(int u,int fa)
maxp[u] = max(maxp[u],su - size[u]);
if (maxp[u] < maxp[rt])
rt = u;
}void get_size(int u,int fa)
}void dfs(int u,int fa,int w)
if (!cnt[c[rt]])
ros += w;
vector ::iterator it;
for (it = d[u].begin();it != d[u].end();it++)
cnt[c[u]]--;
}void upd(int u,int fa,int co,int su)
ans[u] += sum - su + co * x;
if (!cnt[c[rt]])
ans[u] += ros;
vector ::iterator it;
for (it = d[u].begin();it != d[u].end();it++)
cnt[c[u]]--;
}void calc(int u)
for (it = d[u].begin();it != d[u].end();it++)
ans[u] += sum - s[c[u]] + size[u];
for (it = d[u].begin();it != d[u].end();it++)
}void solve(int u)
}int main()
su = n;
maxp[0] = n;
get_rt(1,0);
get_size(rt,0);
solve(rt);
for (int i = 1;i <= n;i++)
printf("%lld\n",ans[i]);
return 0;
}
P2664 樹上遊戲
分析 點分治。首先關於答案的統計轉化成計算每個顏色的貢獻。1 計算從根出發的路徑的答案 如果某乙個顏色是從根到這個點的鏈上的第一次出現的,那麼這個顏色會對根產生siz x 個貢獻。根連向它子樹的任意乙個點的路徑都包含這個顏色 2 計算子樹內每個點過根的路徑答案 記錄乙個陣列sum i 表示從根出發包...
P2664 樹上遊戲
題面 作為一道經典的點分治題目,此題能很好的考察對點分治的運用。個人認為點分治的本質在於 對於樹上近乎n2 的路徑詢問,通過有效的 劃分,使之能在穩定的時間內通過儲存資訊 獲取資訊的經典方式來求 出答案。由此看出點分治的關鍵在於儲存資訊與獲取資訊的方式。點分治的模板套上之後我們只需要考慮的是子樹與子...
洛谷 P2664 樹上遊戲 解題報告
text 有一棵樹,樹的每個節點有個顏色。給乙個長度為 n 的顏色序列,定義 s i,j 為 i 到 j 的顏色數量。以及 sum i sum limits ns i,j 現在他想讓你求出所有的 sum i 第一行為乙個整數 n 表示樹節點的數量 第二行為 n 個整數,分別表示 n 個節點的顏色 c...