HDU - 4358 Boring counting (树上启发式合并/线段树合并)

题目链接

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

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

启发式合并1:(748ms)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=1e5+10;
 5 int n,m,k,a[N],b[N],nb,fa[N],son[N],siz[N],cnt[N],ans[N],now,ne,hd[N],ka;
 6 struct E {int v,nxt;} e[N<<1];
 7 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
 8 void dfs1(int u,int f) {
 9     fa[u]=f,son[u]=0,siz[u]=1;
10     for(int i=hd[u]; ~i; i=e[i].nxt) {
11         int v=e[i].v;
12         if(v==fa[u])continue;
13         dfs1(v,u),siz[u]+=siz[v];
14         if(siz[v]>siz[son[u]])son[u]=v;
15     }
16 }
17 void add(int x,int dx) {
18     if(cnt[x]==k)--now;
19     cnt[x]+=dx;
20     if(cnt[x]==k)++now;
21 }
22 void cal(int u,int x) {
23     add(a[u],x);
24     for(int i=hd[u]; ~i; i=e[i].nxt) {
25         int v=e[i].v;
26         if(v!=fa[u])cal(v,x);
27     }
28 }
29 void dfs2(int u,int f) {
30     for(int i=hd[u]; ~i; i=e[i].nxt) {
31         int v=e[i].v;
32         if(v!=fa[u]&&v!=son[u])dfs2(v,0);
33     }
34     if(son[u])dfs2(son[u],1);
35     add(a[u],1);
36     for(int i=hd[u]; ~i; i=e[i].nxt) {
37         int v=e[i].v;
38         if(v!=fa[u]&&v!=son[u])cal(v,1);
39     }
40     ans[u]=now;
41     if(!f) {
42         add(a[u],-1);
43         for(int i=hd[u]; ~i; i=e[i].nxt) {
44             int v=e[i].v;
45             if(v!=fa[u])cal(v,-1);
46         }
47     }
48 }
49 int main() {
50     int T;
51     for(scanf("%d",&T); T--;) {
52         if(ka)puts("");
53         printf("Case #%d:\n",++ka);
54         memset(hd,-1,sizeof hd),ne=0;
55         memset(cnt,0,sizeof cnt);
56         memset(ans,0,sizeof ans),now=0;
57         scanf("%d%d",&n,&k);
58         for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
59         for(int i=1; i<=n; ++i)b[i]=a[i];
60         sort(b+1,b+1+n),nb=unique(b+1,b+1+n)-(b+1);
61         for(int i=1; i<=n; ++i)a[i]=lower_bound(b+1,b+1+nb,a[i])-b;
62         for(int i=1; i<n; ++i) {
63             int u,v;
64             scanf("%d%d",&u,&v);
65             addedge(u,v),addedge(v,u);
66         }
67         dfs1(1,-1),dfs2(1,1);
68         scanf("%d",&m);
69         while(m--) {
70             int u;
71             scanf("%d",&u);
72             printf("%d\n",ans[u]);
73         }
74     }
75     return 0;
76 }

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

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=1e5+10;
 5 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];
 6 struct E {int v,nxt;} e[N<<1];
 7 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
 8 void dfs1(int u,int f) {
 9     fa[u]=f,son[u]=0,siz[u]=1,bg[u]=++tot,rnk[bg[u]]=u;
10     for(int i=hd[u]; ~i; i=e[i].nxt) {
11         int v=e[i].v;
12         if(v==fa[u])continue;
13         dfs1(v,u),siz[u]+=siz[v];
14         if(siz[v]>siz[son[u]])son[u]=v;
15     }
16     ed[u]=tot;
17 }
18 void add(int x,int dx) {
19     if(cnt[x]==k)--now;
20     cnt[x]+=dx;
21     if(cnt[x]==k)++now;
22 }
23 void dfs2(int u,int f) {
24     for(int i=hd[u]; ~i; i=e[i].nxt) {
25         int v=e[i].v;
26         if(v!=fa[u]&&v!=son[u])dfs2(v,0);
27     }
28     if(son[u])dfs2(son[u],1);
29     add(a[u],1);
30     for(int i=hd[u]; ~i; i=e[i].nxt) {
31         int v=e[i].v;
32         if(v!=fa[u]&&v!=son[u])for(int i=bg[v]; i<=ed[v]; ++i)add(a[rnk[i]],1);
33     }
34     ans[u]=now;
35     if(!f)for(int i=bg[u]; i<=ed[u]; ++i)add(a[rnk[i]],-1);
36 }
37 int main() {
38     int T;
39     for(scanf("%d",&T); T--;) {
40         if(ka)puts("");
41         printf("Case #%d:\n",++ka);
42         memset(hd,-1,sizeof hd),ne=0;
43         memset(cnt,0,sizeof cnt),tot=0;
44         memset(ans,0,sizeof ans),now=0;
45         scanf("%d%d",&n,&k);
46         for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
47         for(int i=1; i<=n; ++i)b[i]=a[i];
48         sort(b+1,b+1+n),nb=unique(b+1,b+1+n)-(b+1);
49         for(int i=1; i<=n; ++i)a[i]=lower_bound(b+1,b+1+nb,a[i])-b;
50         for(int i=1; i<n; ++i) {
51             int u,v;
52             scanf("%d%d",&u,&v);
53             addedge(u,v),addedge(v,u);
54         }
55         dfs1(1,-1),dfs2(1,1);
56         scanf("%d",&m);
57         while(m--) {
58             int u;
59             scanf("%d",&u);
60             printf("%d\n",ans[u]);
61         }
62     }
63     return 0;
64 }

启发式合并3(map版):(1263ms)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=1e5+10;
 5 int n,m,k,a[N],b[N],F[N],nb,ans[N],ne,hd[N],ka;
 6 map<int,int> mp[N];
 7 struct E {int v,nxt;} e[N<<1];
 8 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
 9 void add(int u,int x,int dx) {
10     if(mp[F[u]][x]==k)--ans[u];
11     mp[F[u]][x]+=dx;
12     if(mp[F[u]][x]==k)++ans[u];
13 }
14 void mg(int u,int v) {
15     if(mp[F[u]].size()<mp[F[v]].size())ans[u]=ans[v],swap(F[u],F[v]);
16     for(auto p:mp[F[v]])add(u,p.first,p.second);
17     mp[F[v]].clear();
18 }
19 void dfs(int u,int fa) {
20     add(F[u]=u,a[u],1);
21     for(int i=hd[u]; ~i; i=e[i].nxt) {
22         int v=e[i].v;
23         if(v==fa)continue;
24         dfs(v,u),mg(u,v);
25     }
26 }
27 int main() {
28     int T;
29     for(scanf("%d",&T); T--;) {
30         if(ka)puts("");
31         printf("Case #%d:\n",++ka);
32         memset(hd,-1,sizeof hd),ne=0;
33         memset(ans,0,sizeof ans);
34         scanf("%d%d",&n,&k);
35         for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
36         for(int i=1; i<=n; ++i)b[i]=a[i];
37         sort(b+1,b+1+n),nb=unique(b+1,b+1+n)-(b+1);
38         for(int i=1; i<=n; ++i)a[i]=lower_bound(b+1,b+1+nb,a[i])-b;
39         for(int i=1; i<n; ++i) {
40             int u,v;
41             scanf("%d%d",&u,&v);
42             addedge(u,v),addedge(v,u);
43         }
44         dfs(1,-1),mp[F[1]].clear();
45         scanf("%d",&m);
46         while(m--) {
47             int u;
48             scanf("%d",&u);
49             printf("%d\n",ans[u]);
50         }
51     }
52     return 0;
53 }

线段树合并:(1014ms)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=1e5+10;
 5 int n,m,k,a[N],b[N],nb,ans[N],ne,hd[N],ka,tot,rt[N],ls[N*20],rs[N*20],val[N*20],sum[N*20];
 6 #define mid ((l+r)>>1)
 7 struct E {int v,nxt;} e[N<<1];
 8 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
 9 void pu(int u) {sum[u]=sum[ls[u]]+sum[rs[u]];}
10 int newnode() {int u=++tot; ls[u]=rs[u]=val[u]=sum[u]=0; return u;}
11 void add(int& u,int x,int dx,int l=1,int r=nb) {
12     if(!u)u=newnode();
13     if(l==r) {val[u]+=dx,sum[u]=val[u]==k; return;}
14     x<=mid?add(ls[u],x,dx,l,mid):add(rs[u],x,dx,mid+1,r);
15     pu(u);
16 }
17 void mg(int& u,int v,int l=1,int r=nb) {
18     if(!u||!v) {u=u|v; return;}
19     if(l==r) {val[u]+=val[v],sum[u]=val[u]==k; return;}
20     mg(ls[u],ls[v],l,mid),mg(rs[u],rs[v],mid+1,r),pu(u);
21 }
22 void dfs(int u,int fa) {
23     rt[u]=0,add(rt[u],a[u],1);
24     for(int i=hd[u]; ~i; i=e[i].nxt) {
25         int v=e[i].v;
26         if(v==fa)continue;
27         dfs(v,u),mg(rt[u],rt[v]);
28     }
29     ans[u]=sum[rt[u]];
30 }
31 int main() {
32     int T;
33     for(scanf("%d",&T); T--;) {
34         if(ka)puts("");
35         printf("Case #%d:\n",++ka);
36         memset(hd,-1,sizeof hd),ne=0;
37         memset(ans,0,sizeof ans),tot=0;
38         scanf("%d%d",&n,&k);
39         for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
40         for(int i=1; i<=n; ++i)b[i]=a[i];
41         sort(b+1,b+1+n),nb=unique(b+1,b+1+n)-(b+1);
42         for(int i=1; i<=n; ++i)a[i]=lower_bound(b+1,b+1+nb,a[i])-b;
43         for(int i=1; i<n; ++i) {
44             int u,v;
45             scanf("%d%d",&u,&v);
46             addedge(u,v),addedge(v,u);
47         }
48         dfs(1,-1);
49         scanf("%d",&m);
50         while(m--) {
51             int u;
52             scanf("%d",&u);
53             printf("%d\n",ans[u]);
54         }
55     }
56     return 0;
57 }

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

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=1e5+10;
 5 int n,m,k,a[N],b[N],nb,ne,hd[N],ka,tot,rt[N],ls[N*40],rs[N*40],val[N*40],sum[N*40];
 6 #define mid ((l+r)>>1)
 7 struct E {int v,nxt;} e[N<<1];
 8 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
 9 void pu(int u) {sum[u]=sum[ls[u]]+sum[rs[u]];}
10 int newnode() {int u=++tot; ls[u]=rs[u]=val[u]=sum[u]=0; return u;}
11 void add(int& w,int u,int x,int dx,int l=1,int r=nb) {
12     w=newnode();
13     if(l==r) {val[w]=val[u]+dx,sum[w]=val[w]==k; return;}
14     if(x<=mid)add(ls[w],ls[u],x,dx,l,mid),rs[w]=rs[u];
15     else add(rs[w],rs[u],x,dx,mid+1,r),ls[w]=ls[u];
16     pu(w);
17 }
18 void mg(int& w,int u,int v,int l=1,int r=nb) {
19     if(!u||!v) {w=u|v; return;}
20     w=newnode();
21     if(l==r) {val[w]=val[u]+val[v],sum[w]=val[w]==k; return;}
22     mg(ls[w],ls[u],ls[v],l,mid),mg(rs[w],rs[u],rs[v],mid+1,r),pu(w);
23 }
24 void dfs(int u,int fa) {
25     rt[u]=0,add(rt[u],rt[u],a[u],1);
26     for(int i=hd[u]; ~i; i=e[i].nxt) {
27         int v=e[i].v;
28         if(v==fa)continue;
29         dfs(v,u),mg(rt[u],rt[u],rt[v]);
30     }
31 }
32 int main() {
33     int T;
34     for(scanf("%d",&T); T--;) {
35         if(ka)puts("");
36         printf("Case #%d:\n",++ka);
37         memset(hd,-1,sizeof hd),ne=0;
38         tot=0;
39         scanf("%d%d",&n,&k);
40         for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
41         for(int i=1; i<=n; ++i)b[i]=a[i];
42         sort(b+1,b+1+n),nb=unique(b+1,b+1+n)-(b+1);
43         for(int i=1; i<=n; ++i)a[i]=lower_bound(b+1,b+1+nb,a[i])-b;
44         for(int i=1; i<n; ++i) {
45             int u,v;
46             scanf("%d%d",&u,&v);
47             addedge(u,v),addedge(v,u);
48         }
49         dfs(1,-1);
50         scanf("%d",&m);
51         while(m--) {
52             int u;
53             scanf("%d",&u);
54             printf("%d\n",sum[rt[u]]);
55         }
56     }
57     return 0;
58 }

原文地址:https://www.cnblogs.com/asdfsag/p/10800982.html

时间: 2024-10-30 09:32:55

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

启发式合并&amp;线段树合并&amp;treap合并&amp;splay合并

启发式合并 有\(n\)个集合,每次让你合并两个集合,或询问一个集合中是否存在某个元素. ? 我们可以用平衡树/set维护集合. ? 对于合并两个\(A,B\),如果\(|A|<|B|\),那么我们就把\(A\)中的每个元素暴力加到\(B\)中,否则就把\(B\)中的元素暴力加到\(A\)中. ? 对于一次把\(A\)中的每个元素暴力加到\(B\)中的操作,\(|A|\)会变成\(|A|+|B|\),也就是说大小至少会翻倍,所以一个元素最多被暴力插入\(\log n\)次.每次插入的时间复杂度是

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

这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例,submit,1A! 哇真的舒服 调试输出懒得删了QwQ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<queue> #include

Luogu4556 雨天的尾巴 树上差分、线段树合并

传送门 一个套路题-- 树上差分+线段树合并即可 注意一个细节:$pushup$的时候如果最大值为$0$一定要把最大值对应的答案也设为$0$,否则会$WA$第二个点 另外如果这道题空限变小了请不要交这份代码,因为这份代码没有卡空间... 1 #include<bits/stdc++.h> 2 #define mid ((l + r) >> 1) 3 //This code is written by Itst 4 using namespace std; 5 6 inline in

P3605 [USACO17JAN]Promotion Counting晋升者计数 线段树合并 or 树状数组

题意:每个点有一个权值    求每个节点的子树中比其权值大的节点数 线段树合并模板题 #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);--i) #define ll long long #define see(x) (cerr<<(#x)<<'='

HDU 4358 Boring counting(树状数组)

题意:  给定一棵树,每个节点有一个点权,然后有一些询问,求以某个点为根的子树中有多少的数出现了恰好k次. 思路: 首先dfs一次将树形结构转化成线性结构,利用时间戳记录下以结点u为根的子树在数组中的开始位置和结束位置. 那么我们将所有查询记录下来离线来做,将所有的查询按右端点升序排序. 考虑用树状数组来做这道题,每个位置记录当前从1到当前位置有多少数出现了恰好k次. 从头遍历一遍数组,map离散化记录每个值出现的位置,对于每个位置,如果值出现的次数t大于k,那么在将第t-k次出现的位置加一

[树上差分][线段树合并]JZOJ 3397 雨天的尾巴

Description 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连 根拔起,以及田地里的粮食被弄得一片狼藉. 无奈的深绘里和村民们只好等待救济粮来维生. 不过救济粮的发放方式很特别. 首先村落里的一共有n 座房屋,并形成一个树状结构.然后救济粮分m 次发放,每次选择 两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮.

[线段树合并] Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数

给一棵 N 个点的树,每个点有一个权值,求每个点的子树中有多少个点的权值比它大. 考虑线段树合并,将权值离散化,每个点开一棵权值线段树. 求答案时直接在权值线段树上查询,线段树合并时类似于可并堆. 要注意的是线段树要动态开点,合并时别忘了 up. 内存什么的最好算一下,数组别开小了. 1 #include<bits/stdc++.h> 2 #define rep(i,a,b) for(register int i=a;i<=b;++i) 3 #define rpd(i,a,b) for(

BZOJ2243 [SDOI2011]染色(树链剖分+线段树合并)

题目链接 BZOJ2243 树链剖分+线段树合并 线段树合并的一些细节需要注意一下 #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 100010; int n, m,

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.