[线段树合并] 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(register int i=a;i>=b;--i)
 4 #define rep1(i,x) for(register int i=head[x];i;i=nxt[i])
 5 typedef long long ll;
 6 const int N=2e5+5;
 7 using namespace std;
 8 inline int read(){
 9     int x=0,f=1;char ch=getchar();
10     while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
11     while(isdigit(ch)){x=x*10+ch-‘0‘;ch=getchar();}
12     return x*f;
13 }
14 struct Tree{int x,l,r;}t[N<<4];
15 int n,tot,cnt;int a[N],b[N],head[N],nxt[N],to[N],rt[N],ans[N];
16 void add(int u,int v){nxt[++tot]=head[u];head[u]=tot;to[tot]=v;}
17 void up(int k){t[k].x=t[t[k].l].x+t[t[k].r].x;}
18 void build(int &k,int l,int r,int x){
19     k=++cnt;if(l==r){t[k].x=1;return;}
20     int mid=l+r>>1;
21     if(mid>=x)build(t[k].l,l,mid,x);
22     else build(t[k].r,mid+1,r,x);
23     up(k);
24 }
25 int find(int k,int l,int r,int L,int R){
26     if(L<=l&&r<=R)return t[k].x;
27     if(l>R||r<L)return 0;
28     int mid=l+r>>1;
29     return find(t[k].l,l,mid,L,R)+find(t[k].r,mid+1,r,L,R);
30 }
31 int merge(int x,int y){
32     if(!x||!y)return x+y;
33     t[x].x+=t[y].x;
34     t[x].l=merge(t[x].l,t[y].l);
35     t[x].r=merge(t[x].r,t[y].r);
36     up(x);
37     return x;
38 }
39 void dfs(int x){
40     rep1(i,x){
41         int p=to[i];dfs(p);
42         rt[x]=merge(rt[x],rt[p]);
43     }
44     int k=lower_bound(b+1,b+n+1,a[x])-b;
45     ans[x]=find(rt[x],1,n,k+1,n);
46 }
47 int main(){
48     n=read();rep(i,1,n)b[i]=a[i]=read();
49     sort(b+1,b+n+1);
50     rep(i,1,n){
51         int k=lower_bound(b+1,b+n+1,a[i])-b;
52         build(rt[i],1,n,k);
53     }
54     rep(i,2,n)add(read(),i);dfs(1);
55     rep(i,1,n)printf("%d\n",ans[i]);
56     return 0;
57 }

原文地址:https://www.cnblogs.com/maximumhanyu/p/11433029.html

时间: 2024-07-29 09:57:48

[线段树合并] Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数的相关文章

洛谷P3605 [USACO17JAN]Promotion Counting晋升者计数

洛谷P3605 [USACO17JAN]Promotion Counting晋升者计数 题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 $1 \cdots N(1 \leq N \leq 100, 000)$ 编号,把公司组织成一棵树,1 号奶牛作为总裁(这棵树的根节点).除了总裁以外的每头奶牛都有一个单独的上司(它在树上的 “双亲结点”).所有的第 $i$ 头牛都有一个不同的能力指数 $p(i)$,描述了她对其工作的擅长程度

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)<<'='

题解 P3605 【[USACO17JAN]Promotion Counting晋升者计数】

这道题开10倍左右一直MLE+RE,然后尝试着开了20倍就A了...窒息 对于这道题目,我们考虑使用线段树合并来做. 所谓线段树合并,就是把结构相同的线段树上的节点的信息合在一起,合并的方式比较类似左偏树什么的. 我们对于每个节点用权值线段树查询大于它的子节点数量,然后把当前节点并到它的父亲上面去. 对于此类型的题目我们通常使用动态开点的线段树(不然炸的没边). 时间复杂度应该是O(nlogn) AC代码如下: 455ms 32824kb 1 #include <bits/stdc++.h>

[USACO17JAN]Promotion Counting晋升者计数

题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 1 \cdots N(1 \leq N \leq 100, 000)1?N(1≤N≤100,000) 编号,把公司组织成一棵树,1 号奶牛作为总裁(这棵树的根节点).除了总裁以外的每头奶牛都有一个单独的上司(它在树上的 "双亲结点").所有的第 ii 头牛都有一个不同的能力指数 p(i)p(i),描述了她对其工作的擅长程度.如果奶牛 ii 是奶牛 jj 的祖先节点(例如

[USACO17JAN] Promotion Counting晋升者计数 (树状数组+dfs)

题目大意:给你一棵树,求以某节点为根的子树中,权值大于该节点权值的节点数 本题考查dfs的性质 离散+树状数组求逆序对 先离散 我们发现,求逆序对时,某节点的兄弟节点会干扰答案 所以,我们在递推时统计一次答案,递归时再统计一次答案,两者的差值就是最终结果 #include <bits/stdc++.h> #define dd double #define N 100100 using namespace std; int n,cnt,ma,lst; int a[N],head[N],s[N],

线段树合并浅谈

对于某些对子树的统计问题,我们固然可以用DSU on Tree来解决,但是一旦带上修改,甚至是加上历史化版本的查询,我们就不得不求助于其他的算法,本篇将对线段树合并进行讲解 线段树合并一般用于对子树的统计,一般的套路就是对树的每一个节点都开上一颗动态开点线段树,然后统计子树信息时,合并所有儿子信息,统计答案,然后继续向上走; 例题也很多,比如[USACO17JAN]Promotion Counting晋升者计数,[NOIP2016 DAY1]天天爱跑步等等都可以这样乱搞,实现起来也非常好理解,这

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 {

CF666E Forensic Examination(广义后缀自动机+线段树合并)

Luogu 给你一个串 $ S $ 以及一个字符串数组 $ T_1 ~ T_m $ , $ q $ 次询问,每次问 $ S $ 的子串S[p_l,p_r]在 $ T_l ~ T_r $ 中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 题解时间 SAM的毒瘤题,无论是倍增来满足长度限制,线段树合并来求区间询问,应有尽有... 对于 $ T $ 串建广义SAM,之后考虑如何使得 $ S $ 在SAM上匹配时求出 $ S $ 在每个 $ T $ 的出现次数. 很明显用线段树

【BZOJ4399】魔法少女LJJ 线段树合并

[BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味:小猴在枝头悠来荡去,好不自在:各式各样的鲜花争相开放,各种树枝的枝头挂满沉甸甸的野果:鸟儿的歌声婉转动听,小河里飘着落下的花瓣真是人间仙境”SHY觉得LJJ还是太naive,一天,SHY带着自己心爱的图找到LJJ,对LJJ说:“既然你已经见识过动态树