生活随笔
收集整理的這篇文章主要介紹了
【CF375D】Trees and Queries——树上启发式合并
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
(題面不是來自Luogu)
題目描述
有一個大小為n且以1為根的樹,樹上每個點都有對應(yīng)的顏色ci 。現(xiàn)給出m次詢問v, k,問以v為根的子樹中有多少種顏色至少出現(xiàn)了k次。
輸入格式
第一行兩個數(shù)n,m表示樹的大小以及詢問的次數(shù)。
第二行n個數(shù)表示樹上每個結(jié)點的顏色。
接下來的n-1行,每行兩個數(shù)a, b表示樹上的邊。
接下來m行,每行兩個數(shù)v, k表示詢問。
輸出格式
m行,每行一個數(shù)表示第i次詢問的答案。
樣例輸入 1
8 5 1 2 2 3 3 2 3 3 1 2 1 5 2 3 2 4 5 6 5 7 5 8 1 2 1 3 1 4 2 3 5 3
樣例輸出 1
2 2 1 0 1
?
樣例輸入 2
4 1 1 2 3 4 1 2 2 3 3 4 1 1
樣例輸出 2
4
數(shù)據(jù)范圍
2≤n≤100000
1≤m≤100000
1≤ci ≤100000
1≤a, b≤n, a≠b
1≤v≤n, 1≤k≤100000
對于其中30%的數(shù)據(jù)保證n,m≤100且ci ≤n
對于其中60%的數(shù)據(jù)保證n≤5000
?
(7:20)今天為了打這個題的正解學(xué)了dsu on tree。需要學(xué)習(xí)的同學(xué)請看我的上一篇博客。
樹上啟發(fā)式合并主要解決的就是這類靜態(tài)子樹統(tǒng)計問題。
對于這題,我們維護兩個數(shù)組cnt和upr,分別表示某種顏色出現(xiàn)的數(shù)量和出現(xiàn)次數(shù)大于某個次數(shù)的顏色的種類數(shù)。每次暴力累計子樹信息時,出現(xiàn)一個顏色先++cnt[color[i]],然后++upr[color[i]]。容易想到,這樣可以保證不重不漏地統(tǒng)計到所有達到upr[i]的顏色,并且不會重復(fù)累加。剩下的套dsu on tree板子即可。
代碼:
#include?<cstdio>?? #include?<iostream>?? #include?<cctype>?? #include?<cstring>?? #include?<vector>?? #define?maxn?100010?? using?namespace?std;?? template?<class?T>?? void?read(T?&x)?{?? ????x?=?0;?? ????char?ch?=?getchar();?? ????while?(!isdigit(ch))?? ????????ch?=?getchar();?? ????while?(isdigit(ch))?{?? ????????x?=?x?*?10?+?(ch?^?48);?? ????????ch?=?getchar();?? ????}?? }?? int?n,?m,?color[maxn],?upr[maxn],?cnt[maxn],?ans[maxn];?? struct?Query?{?? ????int?k,?id;?? };?? vector<Query>?Q[maxn];?? int?head[maxn],?top;?? struct?E?{?? ????int?to,?nxt;?? }?edge[maxn?<<?1];?? inline?void?insert(int?u,?int?v)?{?? ????edge[++top]?=?(E)?{v,?head[u]};?? ????head[u]?=?top;?? }?? int?size[maxn],?son[maxn];?? void?dfs(int?u,?int?pre)?{?? ????size[u]?=?1;?? ????for?(int?i?=?head[u];?i;?i?=?edge[i].nxt)?{?? ????????int?v?=?edge[i].to;?? ????????if?(v?==?pre)?continue;?? ????????dfs(v,?u);?? ????????size[u]?+=?size[v];?? ????????if?(size[son[u]]?<?size[v])?? ????????????son[u]?=?v;?? ????}?? }?? int?CurSon;?? void?cal(int?u,?int?pre,?int?val)?{?? ????if?(val?==?-1)??? ????????--upr[cnt[color[u]]];?? ????cnt[color[u]]?+=?val;?? ????if?(val?==?1)?? ????????++upr[cnt[color[u]]];?? ????for?(int?i?=?head[u];?i;?i?=?edge[i].nxt)?{?? ????????int?v?=?edge[i].to;?? ????????if?(v?==?CurSon?||?v?==?pre)?continue;?? ????????cal(v,?u,?val);?? ????}?? }?? void?dsu(int?u,?int?pre,?bool?op)?{?? ????for?(int?i?=?head[u];?i;?i?=?edge[i].nxt)?{?? ????????int?v?=?edge[i].to;?? ????????if?(v?==?pre?||?v?==?son[u])?continue;?? ????????dsu(v,?u,?0);?? ????}?? ????if?(son[u])?? ????????dsu(son[u],?u,?1),?CurSon?=?son[u];?? ????cal(u,?pre,?1);?CurSon?=?0;?? ????for?(int?i?=?0;?i?<?Q[u].size();?++i)??? ????????ans[Q[u][i].id]?=?upr[Q[u][i].k];?? ????if?(!op)?? ????????cal(u,?pre,?-1);?? }?? int?main()?{?? ????read(n),?read(m);?? ????for?(int?i?=?1;?i?<=?n;?++i)??? ????????read(color[i]);?? ????int?u,?v;?? ????for?(int?i?=?1;?i?<?n;?++i)?{?? ????????read(u),?read(v);?? ????????insert(u,?v),?insert(v,?u);?? ????}?? ????int?k;?? ????for?(int?i?=?1;?i?<=?m;?++i)?{?? ????????read(v),?read(k);?? ????????Q[v].push_back((Query)?{k,?i});?? ????}?? ????dfs(1,?0);?? ????dsu(1,?0,?1);?? ????for?(int?i?=?1;?i?<=?m;?++i)?? ????????printf("%d\n",?ans[i]);?? ????return?0;?? }??
轉(zhuǎn)載于:https://www.cnblogs.com/TY02/p/11219304.html
總結(jié)
以上是生活随笔 為你收集整理的【CF375D】Trees and Queries——树上启发式合并 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。