前排感谢AgOH大佬 orz 【AgOHの算法胡扯】树上启发式合并(dsu on tree)_哔哩哔哩_bilibili
思想很简单:大的向小的上面合并,根据轻重链的性质可以证明时间复杂度缩减为O(nlogn)具体证明可自行搜索。
题目:[P9233 蓝桥杯 2023 省 A] 颜色平衡树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
理解AgOH大佬的讲的模板题,这道题就会简单不少,唯一难点是判断颜色平衡树:
我们设计三个数组: color[i],cnt[i],cntcnt[i],color[i]表示节点i的颜色,cnt[i]表示颜色i出现的次数,cntcnt[i]表示 颜色出现次数 i 的出现的次数。
对于以x为根的子树,如果 设 **c=cnt[color[x]] , f=cntcnt[c] 如果 c*fsize[x] 那么他就是颜色平衡树。**原因:假设存在一个颜色出现的次数不等于c,那么必然 cf < size[x],这与cfsize[x]矛盾,所以假设不成立。
当然,我们看到颜色个数这种问题,自然而然会想起来莫队的模板题([P1903 国家集训队] 数颜色 / 维护队列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)),这题也可以用莫队解决,只是需要先dfs一遍进行线性化而已,还是上面一样的判断方法,判断的时间复杂度为O(1),整体时间复杂度为O(n*根号n),可以通过本题。唯一需要注意一点的是,为了操作方便可能还要多存一个dfx表示时间戳x的原来的节点编号。
法一:启发式合并AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
| #include <iostream> #include <cmath> #include <ctime> #include <string> #include <cstring> #include <vector> #include <algorithm>
#define ll long long
using namespace std;
const int maxn=200000+10; vector<int> graph[maxn];
int n,root=1; int color[maxn],son[maxn],t[2][maxn],siz[maxn];
int ans=0,flag;
void add(int x) { t[1][t[0][x]]--; t[0][x]++; t[1][t[0][x]]++; }
void del(int x) { t[1][t[0][x]]--; t[0][x]--; t[1][t[0][x]]++; }
void add_k(int now) { add(color[now]);
for(const auto&it : graph[now]) if(it!=flag)add_k(it); }
void del_k(int now) { del(color[now]);
for(const auto&it : graph[now]) if(it!=flag)del_k(it); }
void dfs(int now) { int maxsize=-1; siz[now]=1;
for(const auto&it : graph[now]) { dfs(it); siz[now]+=siz[it]; if(siz[it]>maxsize) { maxsize=siz[it]; son[now]=it; } } }
void dfs(int now,bool keep) { for(const auto& it : graph[now]) if(it != son[now]) dfs(it,false); if(son[now]!=0) { dfs(son[now],true); flag=son[now]; }
add_k(now); flag=0; if(t[0][color[now]]*t[1][t[0][color[now]]]==siz[now]) ++ans;
if(keep==false) { del_k(now); } }
int main() { ios::sync_with_stdio(false); cin.tie(0),cout.tie(0);
#ifdef LOCAL clock_t c1 = clock(); freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif
cin>>n;
for(int i=1,f;i<=n;++i) { cin>>color[i]>>f; graph[f].push_back(i); }
dfs(root); dfs(root,true);
cout << ans << endl;
#ifdef LOCAL cout << "Time Used:\n" << clock() - c1 << " ms" << endl; #endif
return 0; }
|

法二:莫队AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| #include <iostream> #include <cmath> #include <ctime> #include <string> #include <cstring> #include <vector> #include <algorithm>
#define ll long long
using namespace std; const int maxn=200000+10; vector<int> graph[maxn];
int n,root=1,tim=0,block; int color[maxn],siz[maxn],dfn[maxn],dfx[maxn]; int cnt[maxn],cntcnt[maxn];
struct QUERY{ int l,r; bool operator<(const QUERY& other)const{ if(l/block==other.l/block)return r<other.r; return l<other.l; } }query[maxn];
void dfs(int now) { siz[now]=1; dfn[now]=++tim; dfx[tim]=now;
for(const auto&it : graph[now]) { dfs(it); siz[now]+=siz[it]; } }
void add(int x,int k) { cntcnt[cnt[x]]--; cnt[x]+=k; cntcnt[cnt[x]]++; }
int main() { ios::sync_with_stdio(false); cin.tie(0),cout.tie(0);
#ifdef LOCAL clock_t c1 = clock(); freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif
cin>>n;
block=sqrt(n);
for(int i=1,y;i<=n;++i) { cin>>color[i]>>y; graph[y].push_back(i); }
dfs(root);
for(int i=1;i<=n;++i) query[i]={dfn[i],dfn[i]+siz[i]-1};
sort(query+1,query+1+n);
int ans=0,l=1,r=1; add(color[dfn[1]],1);
for(int i=1,c,f;i<=n;++i) { while(l<query[i].l)add(color[dfx[l++]],-1); while(l>query[i].l)add(color[dfx[--l]],1); while(r<query[i].r)add(color[dfx[++r]],1); while(r>query[i].r)add(color[dfx[r--]],-1);
c=cnt[color[dfx[query[i].l]]],f=cntcnt[c];
if(c*f==query[i].r-query[i].l+1) ++ans; }
cout<<ans;
#ifdef LOCAL cout << "\nTime Used: \n" << clock() - c1 << " ms" << endl; #endif
return 0; }
|
