HDU - 4358 Boring counting (树上启发式合并/线段树合并)
题意:统计树上每个结点中恰好出现了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 (树上启发式合并/线段树合并)的更多相关文章
- 洛谷P3605 [USACO17JAN] Promotion Counting 晋升者计数 [线段树合并]
题目传送门 Promotion Counting 题目描述 The cows have once again tried to form a startup company, failing to r ...
- hdu 5511 Minimum Cut-Cut——分类讨论思想+线段树合并
题目:http://acm.hdu.edu.cn/showproblem.php?pid=5511 题意:割一些边使得无向图变成不连通的,并且恰好割了两条给定生成树上的边.满足非树边两段一定在给定生成 ...
- 启发式合并 splay合并 线段树合并基础
Gold is everywhen! - somebody 启发式合并 将小的集合一个个插入到大的集合. 每次新集合大小至少比小集合大一倍,因此每个元素最多合并\(\log n\)次,总复杂度为\(n ...
- 启发式合并&线段树合并/分裂&treap合并&splay合并
启发式合并 有\(n\)个集合,每次让你合并两个集合,或询问一个集合中是否存在某个元素. 我们可以用平衡树/set维护集合. 对于合并两个\(A,B\),如果\(|A|<|B|\),那么 ...
- [BZOJ2733][HNOI2010]永无乡 解题报告 启发式合并,线段树合并
好久没更新博客了,前段时间一直都在考试,都没时间些,现在终于有点闲了(cai guai)... 写了一道题,[HNOI2012]永无乡,其实是一道板子题,我发现我写了好多板子题...还是太菜了... ...
- bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)
这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例, ...
- Luogu5327【ZJOI2019】语言【树上差分,线段树合并】
题目大意 给定一棵$n$个节点的树,维护$n$个集合,一开始第$i$个集合只有节点$i$.有$m$个操作,每次操作输入一个$(u,v)$,表示将$(u,v)$这条链上所有点所属的集合合并.求有多少个无 ...
- 5.20 省选模拟赛 T1 图 启发式合并 线段树合并 染色计数问题
LINK:图 在说这道题之前吐槽一下今天的日子 520 = 1+1+4+514. /cy 这道题今天做的非常失败 一点分都没拿到手 关键是今天的T3 把我整个人给搞崩了. 先考虑 如果得到了这么一张图 ...
- HDU 4358 Boring counting(莫队+DFS序+离散化)
Boring counting Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 98304/98304 K (Java/Others) ...
随机推荐
- Prism 订阅事件 IEventAggregator 说明
本节学习了Event Aggregation事件聚合,这个在Prism中很重要,特别是对于Module间的通信.除了前面介绍的Command可以用于模块间的通信,还有我们这一节介绍的Event Agg ...
- flex label如何通过AS3实现颜色设置
如果你想修改label的颜色,你可以在控件上面直接设置color属性,但是有这样一个情况,有些label的颜色是动态设置的,没办法一开始初始化的时候设置 这个时候就需要在AS当中动态的设置,直接.se ...
- Linux进程:管理和调度
一:进程管理 进程.轻量级进程和线程 通常定义:进程是程序执行时的一个实例. 这个很像类和实例对象的关系.从内核来看:进程的目的就是担当分配系统资源(CPU,内存等)的实体. 当进程创建时,它几乎和父 ...
- #Java学习之路——基础阶段二(第十二篇)
我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...
- 应用安全 - 平台 | 工具 - Centreon Web - 漏洞 - 汇总
简介 产地 法国 用途 监控 分布 CVE-2019-16405 https://thecybergeek.co.uk/cves/2019/09/19/CVEs.html
- HDU 1087 Super Jumping! Jumping! Jumping! (动态规划、最大上升子序列和)
Super Jumping! Jumping! Jumping! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 ...
- RSA加密 抛异常 algid parse error, not a sequence
JDK1.8环境 参考:BouncyCastle的使用:https://blog.csdn.net/qq_29583513/article/details/78866461 可解决 公钥解密 私钥加密 ...
- olap和Oltp(转)
OLAP和OLTP的区别(基础知识) 联机分析处理 (OLAP) 的概念最早是由关系数据库之父E.F.Codd于1993年提出的,他同时提出了关于OLAP的12条准则.OLAP的提出引起了很大的反响, ...
- shiro三连斩之概念
1, 什么是Shiro? Shiro是一个安全框架,用于解决系统的认证和授权问题,同时提供了会话管理,数据加密,与WEB集成,缓存等机制. Authentication:身份认证/登录,验证用户是不是 ...
- JAVA并发编程实战笔记 第二章
2.1 线程安全性 当多个线程访问某个类时,不论这些线程如何交替执行,这个类始终都能表现出正确的行为,且主调代码中不需要任何额外的同步或协同,则称这个类是线程安全的. 类不变性条件(Invariant ...