题目链接

题意:统计树上每个结点中恰好出现了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. VBA删除空白行列

    '删除空行 Sub DeleteEmptyRows() Dim LastRow As Long, r As Long LastRow = ActiveSheet.UsedRange.Rows.Coun ...

  2. iOS发版出现“No iTunes Connect access for the team”的问题的解决方式

    要发个新版本,结果发现,老是提示我“No iTunes Connect access for the team”,出现以下错误:   图1 错误提示: No accounts with iTunes ...

  3. Day01:API文档 / 字符串基本操作

    JDK API 什么是JDK API? JDK中包含大量的API类库,所谓AP就是一些写好的,可提供直接调用的功能(在Java语言中,这些功能以类的形式封装). JDK API包含的类库功能强大,经常 ...

  4. scrapy 正则汉字的提取方法

    [\u4E00-\u9FA5]

  5. HDU 2973 YAPTCHA (威尔逊定理)

    YAPTCHA Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  6. TCP listener

    调试时未关闭之前开启的TCP SERVER: 应该关闭: TcpListener TcpClient 1.开启TCP listener (1)Start()方法失败 异常位置:该异常的产生位置为 tc ...

  7. P1880石子合并

    1995年的noi区间dp题,这道题AC耗时达到了数月. 有一道题叫做果子合并,也是求合并的最小花费,但是那个题是可以随便合并两堆,但是这个题只能合并相邻的两堆,并且是一个环.对于环的问题,我们一般可 ...

  8. Android-Widget桌面小组件

    1, 掌握Widget的用:Widget的用途,能够添加到手机桌面的程序 2, Widget的特点和用法步骤: 特点:快捷,方便,个性化,可自定义功能,可及时控制更新Widget显示内容 3, 用法步 ...

  9. Quatrz + Spring

    实例工程结构: 配置: pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=" ...

  10. 8.jQuery之上下滑动效果

    上下滑动:slideDown   slideUp  slideToggle <style> div { width: 150px; height: 300px; background-co ...