题目链接

题意:统计树上每个结点中恰好出现了k次的颜色数。

dsu on tree/线段树合并裸题。

启发式合并1:(748ms)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
int n,m,k,a[N],b[N],nb,fa[N],son[N],siz[N],cnt[N],ans[N],now,ne,hd[N],ka;
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void dfs1(int u,int f) {
fa[u]=f,son[u]=,siz[u]=;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void add(int x,int dx) {
if(cnt[x]==k)--now;
cnt[x]+=dx;
if(cnt[x]==k)++now;
}
void cal(int u,int x) {
add(a[u],x);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u])cal(v,x);
}
}
void dfs2(int u,int f) {
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u])dfs2(v,);
}
if(son[u])dfs2(son[u],);
add(a[u],);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u])cal(v,);
}
ans[u]=now;
if(!f) {
add(a[u],-);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u])cal(v,-);
}
}
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
if(ka)puts("");
printf("Case #%d:\n",++ka);
memset(hd,-,sizeof hd),ne=;
memset(cnt,,sizeof cnt);
memset(ans,,sizeof ans),now=;
scanf("%d%d",&n,&k);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i]=a[i];
sort(b+,b++n),nb=unique(b+,b++n)-(b+);
for(int i=; i<=n; ++i)a[i]=lower_bound(b+,b++nb,a[i])-b;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs1(,-),dfs2(,);
scanf("%d",&m);
while(m--) {
int u;
scanf("%d",&u);
printf("%d\n",ans[u]);
}
}
return ;
}

启发式合并2(加了dfs序的启发式合并,只比普通的快了一丁点):(702ms)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
int n,m,k,a[N],b[N],nb,fa[N],son[N],siz[N],cnt[N],ans[N],now,ne,hd[N],ka,tot,bg[N],ed[N],rnk[N];
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void dfs1(int u,int f) {
fa[u]=f,son[u]=,siz[u]=,bg[u]=++tot,rnk[bg[u]]=u;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
ed[u]=tot;
}
void add(int x,int dx) {
if(cnt[x]==k)--now;
cnt[x]+=dx;
if(cnt[x]==k)++now;
}
void dfs2(int u,int f) {
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u])dfs2(v,);
}
if(son[u])dfs2(son[u],);
add(a[u],);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u])for(int i=bg[v]; i<=ed[v]; ++i)add(a[rnk[i]],);
}
ans[u]=now;
if(!f)for(int i=bg[u]; i<=ed[u]; ++i)add(a[rnk[i]],-);
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
if(ka)puts("");
printf("Case #%d:\n",++ka);
memset(hd,-,sizeof hd),ne=;
memset(cnt,,sizeof cnt),tot=;
memset(ans,,sizeof ans),now=;
scanf("%d%d",&n,&k);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i]=a[i];
sort(b+,b++n),nb=unique(b+,b++n)-(b+);
for(int i=; i<=n; ++i)a[i]=lower_bound(b+,b++nb,a[i])-b;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs1(,-),dfs2(,);
scanf("%d",&m);
while(m--) {
int u;
scanf("%d",&u);
printf("%d\n",ans[u]);
}
}
return ;
}

启发式合并3(map版,原理与普通启发式合并相同,轻链合并到重链上,重链中的父结点的贡献直接加进子结点):(1185ms)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
int n,m,k,a[N],b[N],F[N],nb,ans[N],ne,hd[N],ka,fa[N],son[N],siz[N];
map<int,int> mp[N];
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void add(int u,int x,int dx) {
if(mp[F[u]][x]==k)--ans[u];
mp[F[u]][x]+=dx;
if(mp[F[u]][x]==k)++ans[u];
}
void mg(int u,int v) {
for(auto p:mp[F[v]])add(u,p.first,p.second);
mp[F[v]].clear();
}
void dfs1(int u,int f) {
fa[u]=f,son[u]=,siz[u]=;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u) {
F[u]=u;
if(son[u])dfs2(son[u]),ans[u]=ans[son[u]],F[u]=F[son[u]];
add(u,a[u],);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u]||v==son[u])continue;
dfs2(v),mg(u,v);
}
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
if(ka)puts("");
printf("Case #%d:\n",++ka);
memset(hd,-,sizeof hd),ne=;
memset(ans,,sizeof ans);
scanf("%d%d",&n,&k);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i]=a[i];
sort(b+,b++n),nb=unique(b+,b++n)-(b+);
for(int i=; i<=n; ++i)a[i]=lower_bound(b+,b++nb,a[i])-b;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs1(,-),dfs2(),mp[F[]].clear();
scanf("%d",&m);
while(m--) {
int u;
scanf("%d",&u);
printf("%d\n",ans[u]);
}
}
return ;
}

启发式合并4(map版,直接根据子树大小进行合并):(1263ms)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
int n,m,k,a[N],b[N],F[N],nb,ans[N],ne,hd[N],ka;
map<int,int> mp[N];
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void add(int u,int x,int dx) {
if(mp[F[u]][x]==k)--ans[u];
mp[F[u]][x]+=dx;
if(mp[F[u]][x]==k)++ans[u];
}
void mg(int u,int v) {
if(mp[F[u]].size()<mp[F[v]].size())ans[u]=ans[v],swap(F[u],F[v]);
for(auto p:mp[F[v]])add(u,p.first,p.second);
mp[F[v]].clear();
}
void dfs(int u,int fa) {
add(F[u]=u,a[u],);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa)continue;
dfs(v,u),mg(u,v);
}
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
if(ka)puts("");
printf("Case #%d:\n",++ka);
memset(hd,-,sizeof hd),ne=;
memset(ans,,sizeof ans);
scanf("%d%d",&n,&k);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i]=a[i];
sort(b+,b++n),nb=unique(b+,b++n)-(b+);
for(int i=; i<=n; ++i)a[i]=lower_bound(b+,b++nb,a[i])-b;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs(,-),mp[F[]].clear();
scanf("%d",&m);
while(m--) {
int u;
scanf("%d",&u);
printf("%d\n",ans[u]);
}
}
return ;
}

线段树合并:(1014ms)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
int n,m,k,a[N],b[N],nb,ans[N],ne,hd[N],ka,tot,rt[N],ls[N*],rs[N*],val[N*],sum[N*];
#define mid ((l+r)>>1)
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void pu(int u) {sum[u]=sum[ls[u]]+sum[rs[u]];}
int newnode() {int u=++tot; ls[u]=rs[u]=val[u]=sum[u]=; return u;}
void add(int& u,int x,int dx,int l=,int r=nb) {
if(!u)u=newnode();
if(l==r) {val[u]+=dx,sum[u]=val[u]==k; return;}
x<=mid?add(ls[u],x,dx,l,mid):add(rs[u],x,dx,mid+,r);
pu(u);
}
void mg(int& u,int v,int l=,int r=nb) {
if(!u||!v) {u=u|v; return;}
if(l==r) {val[u]+=val[v],sum[u]=val[u]==k; return;}
mg(ls[u],ls[v],l,mid),mg(rs[u],rs[v],mid+,r),pu(u);
}
void dfs(int u,int fa) {
rt[u]=,add(rt[u],a[u],);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa)continue;
dfs(v,u),mg(rt[u],rt[v]);
}
ans[u]=sum[rt[u]];
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
if(ka)puts("");
printf("Case #%d:\n",++ka);
memset(hd,-,sizeof hd),ne=;
memset(ans,,sizeof ans),tot=;
scanf("%d%d",&n,&k);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i]=a[i];
sort(b+,b++n),nb=unique(b+,b++n)-(b+);
for(int i=; i<=n; ++i)a[i]=lower_bound(b+,b++nb,a[i])-b;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs(,-);
scanf("%d",&m);
while(m--) {
int u;
scanf("%d",&u);
printf("%d\n",ans[u]);
}
}
return ;
}

还不够爽?再来个资瓷在线查询的可持久化线段树合并怎么样?就是有点吃内存。(1310ms)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
int n,m,k,a[N],b[N],nb,ne,hd[N],ka,tot,rt[N],ls[N*],rs[N*],val[N*],sum[N*];
#define mid ((l+r)>>1)
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void pu(int u) {sum[u]=sum[ls[u]]+sum[rs[u]];}
int newnode() {int u=++tot; ls[u]=rs[u]=val[u]=sum[u]=; return u;}
void add(int& w,int u,int x,int dx,int l=,int r=nb) {
w=newnode();
if(l==r) {val[w]=val[u]+dx,sum[w]=val[w]==k; return;}
if(x<=mid)add(ls[w],ls[u],x,dx,l,mid),rs[w]=rs[u];
else add(rs[w],rs[u],x,dx,mid+,r),ls[w]=ls[u];
pu(w);
}
void mg(int& w,int u,int v,int l=,int r=nb) {
if(!u||!v) {w=u|v; return;}
w=newnode();
if(l==r) {val[w]=val[u]+val[v],sum[w]=val[w]==k; return;}
mg(ls[w],ls[u],ls[v],l,mid),mg(rs[w],rs[u],rs[v],mid+,r),pu(w);
}
void dfs(int u,int fa) {
rt[u]=,add(rt[u],rt[u],a[u],);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa)continue;
dfs(v,u),mg(rt[u],rt[u],rt[v]);
}
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
if(ka)puts("");
printf("Case #%d:\n",++ka);
memset(hd,-,sizeof hd),ne=;
tot=;
scanf("%d%d",&n,&k);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i]=a[i];
sort(b+,b++n),nb=unique(b+,b++n)-(b+);
for(int i=; i<=n; ++i)a[i]=lower_bound(b+,b++nb,a[i])-b;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs(,-);
scanf("%d",&m);
while(m--) {
int u;
scanf("%d",&u);
printf("%d\n",sum[rt[u]]);
}
}
return ;
}

HDU - 4358 Boring counting (树上启发式合并/线段树合并)的更多相关文章

  1. 洛谷P3605 [USACO17JAN] Promotion Counting 晋升者计数 [线段树合并]

    题目传送门 Promotion Counting 题目描述 The cows have once again tried to form a startup company, failing to r ...

  2. hdu 5511 Minimum Cut-Cut——分类讨论思想+线段树合并

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5511 题意:割一些边使得无向图变成不连通的,并且恰好割了两条给定生成树上的边.满足非树边两段一定在给定生成 ...

  3. 启发式合并 splay合并 线段树合并基础

    Gold is everywhen! - somebody 启发式合并 将小的集合一个个插入到大的集合. 每次新集合大小至少比小集合大一倍,因此每个元素最多合并\(\log n\)次,总复杂度为\(n ...

  4. 启发式合并&线段树合并/分裂&treap合并&splay合并

    启发式合并 有\(n\)个集合,每次让你合并两个集合,或询问一个集合中是否存在某个元素. ​ 我们可以用平衡树/set维护集合. ​ 对于合并两个\(A,B\),如果\(|A|<|B|\),那么 ...

  5. [BZOJ2733][HNOI2010]永无乡 解题报告 启发式合并,线段树合并

    好久没更新博客了,前段时间一直都在考试,都没时间些,现在终于有点闲了(cai guai)... 写了一道题,[HNOI2012]永无乡,其实是一道板子题,我发现我写了好多板子题...还是太菜了... ...

  6. bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

    这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例, ...

  7. Luogu5327【ZJOI2019】语言【树上差分,线段树合并】

    题目大意 给定一棵$n$个节点的树,维护$n$个集合,一开始第$i$个集合只有节点$i$.有$m$个操作,每次操作输入一个$(u,v)$,表示将$(u,v)$这条链上所有点所属的集合合并.求有多少个无 ...

  8. 5.20 省选模拟赛 T1 图 启发式合并 线段树合并 染色计数问题

    LINK:图 在说这道题之前吐槽一下今天的日子 520 = 1+1+4+514. /cy 这道题今天做的非常失败 一点分都没拿到手 关键是今天的T3 把我整个人给搞崩了. 先考虑 如果得到了这么一张图 ...

  9. HDU 4358 Boring counting(莫队+DFS序+离散化)

    Boring counting Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 98304/98304 K (Java/Others) ...

随机推荐

  1. 阶段3 2.Spring_10.Spring中事务控制_8 spring基于纯注解的声明式事务控制

    新建项目 把之前项目src下的内容全部复制过来 pom.xml内复制过来 开始配置 新建一个config的包,然后再新建配置文件类SpringConfiguration @Configuration这 ...

  2. 【ZT】Enhancement Framework – Introduction

    Enhancement Framework – Introduction By Naimesh Patel | March 26, 2014 | Enhancement Implementation ...

  3. python关键字参数和位置参数

    关键字参数必须跟随在位置参数后面! 因为python函数在解析参数时, 是按照顺序来的, 位置参数是必须先满足, 才能考虑其他可变参数.,否则报错如下: In [74]: print(s1.forma ...

  4. 作为程序员,你关注哪些国外 IT 网站?

    1.在线编程练习: LintCode——在线刷题网站,阶梯式训练,可帮助你更快速深入地了解各类面试题型,提供专业导师写的最优代码作为参考(Lintcode 标准答案查询——lintcode 的参考答案 ...

  5. Oracle 笔记(三)

    Oracle的数据库对象 七大对象:用户.表.约束.序列.视图.同义词和索引 知识点一:用户  -  User  -  账户.管理员-一切对象的宿主 1.创建用户 ???? 2.授权 ???? 授权+ ...

  6. java:异常机制(try,catch,finally,throw,throws,自定义异常)

    * String类中的格式化字符串的方法: * public static String format(String format, Object... args):使用指定的格式字符串和参数返回一个 ...

  7. 前端深入之css篇丨2020年前,彻底掌握css动画

    马上就2020年了,不知道小伙伴们今年学习了css3动画了吗? 说起来css动画是一个很尬的事,一方面因为公司用css动画比较少,另一方面大部分开发者习惯了用JavaScript来做动画,所以就导致了 ...

  8. 用poi从excel文档导入数据

    import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; impo ...

  9. BZOJ3129方程(SDOI2013)

    https://blog.csdn.net/Maxwei_wzj/article/details/80152116 对变量有上界限制及下界限制.对于下界,可以从总数中减去即可,对于上界,容斥定理.

  10. Java中集合关键字的区别

    1. ArrayList.Vector和Stack有什么区别? 1.ArrayList的方法和实现基本上和Vector一样,底层都是数组的实现(简:API基本一样) ​   2.Stack继承了Vec ...