[BZOJ] 4756: [Usaco2017 Jan]Promotion Counting #线段树合并
Posted Lev今天学习了吗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ] 4756: [Usaco2017 Jan]Promotion Counting #线段树合并相关的知识,希望对你有一定的参考价值。
4756: [Usaco2017 Jan]Promotion Counting
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 298 Solved: 210
[Submit][Status][Discuss]
Description
问对于每个奶牛来说,它的子树中有几个能力值比它大的。
Input
接下来n行为1-n号奶牛的能力值pi
接下来n-1行为2-n号奶牛的经理(树中的父亲)
Output
Sample Input
804289384
846930887
681692778
714636916
957747794
1
1
2
3
Sample Output
0
1
0
0
HINT
Source
Analysis
学习到了一个神级操作 0_o :::::::::::::::::::::::::::::::::::: 线段树合并!
首先我们要给线段树改成:动态开点+散结构+不完整树
我们对一个序列建一棵经典线段树,那么他是长什么样的我们大致都是知道的
但如果要想并查集一样分裂合并的话(分裂还没学呀qwq),首先这棵树得是不完整的
如图,这是一棵经典线段树
那么如果我们对每一个元素建一棵线段树(这样他们最终可以合并成一棵)的话
看起来是这样
上面是两个单个结点的线段树
他们合并后是
那么对于每一层结点,他们只保存这棵线段树内已有的元素的信息
但是就建制而言(就是每一层结点保存的范围),他们是与相同情况下的经典线段树一样的
那么这就显然:原来那套 Lchild = root*2 的方式已经不适用了,应当使用动态开点的方式
关键点一就在此了
题意:查找每一个顶点的子树内数值比该顶点大的点的数量
那么建立一棵权值可合并线段树
权值线段树:指以值为下标的线段树,在经典线段树里需要离散化,但可合并线段树使其实现更加容易
如果是权值线段树的话,那么我们的查询区间就不是元素下标了,而是元素的值域
很棒啊!
具体内容还是需要理解代码,无论是权值线段树还是可合并线段树都没什么太多好讲的
Code
#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #define maxn 1000000 using namespace std; int arr[maxn],disc[maxn],n,ans[maxn]; vector<int> G[maxn]; struct node{ int lc,rc,L,R,sum; }; //namespace SegmentTree{ int root[maxn] = {0},tot = 0; node T[maxn*5]; int maintain(int rt){ T[rt].sum = T[T[rt].lc].sum+T[T[rt].rc].sum; } int build(int L,int R,int pos){ int rt = ++tot,mid = (L+R)/2; T[rt].L = L, T[rt].R = R, T[rt].sum = 0; if(L == R) return T[rt].sum = 1,rt; if(pos <= mid) T[rt].lc = build(L,mid,pos); else T[rt].rc = build(mid+1,R,pos); return maintain(rt),rt; } int query(int rt,int qL,int qR){ if(!rt) return 0; if(qL <= T[rt].L && T[rt].R <= qR) return T[rt].sum; int ret = 0,mid = (T[rt].L+T[rt].R)>>1; if(qL <= mid) ret += query(T[rt].lc,qL,qR); if(qR > mid) ret += query(T[rt].rc,qL,qR); return ret; } int merge(int x,int y){ if(!x||!y) return x^y; T[x].lc = merge(T[x].lc,T[y].lc); T[x].rc = merge(T[x].rc,T[y].rc); return maintain(x),x; } void dfs(int u,int fa){ int res = 0; for(int i = 0;i < G[u].size();i++){ if(G[u][i] == fa) continue; int v = G[u][i]; dfs(v,u); res += query(root[v],arr[u]+1,n); root[v] = merge(root[u],root[v]); }ans[u] = res; } //} int main(){ scanf("%d",&n); for(int i = 1;i <= n;i++) scanf("%d",&arr[i]),disc[i] = arr[i]; sort(disc+1,disc+1+n); int m = unique(disc+1,disc+1+n)-disc-1; for(int i = 1;i <= n;i++) arr[i] = lower_bound(disc+1,disc+1+m,arr[i])-disc; for(int i = 2;i <= n;i++){ int fa; scanf("%d",&fa); G[i].push_back(fa); G[fa].push_back(i); } for(int i = 1;i <= n;i++) root[i] = build(1,n,arr[i]); dfs(1,1); for(int i = 1;i <= n;i++) printf("%d\\n",ans[i]); return 0; }
以上是关于[BZOJ] 4756: [Usaco2017 Jan]Promotion Counting #线段树合并的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4756[Usaco2017 Jan]Promotion Counting 离散化+树状数组
BZOJ_4756_[Usaco2017 Jan]Promotion Counting_树状数组
BZOJ4756: [Usaco2017 Jan]Promotion Counting
BZOJ 4756 [Usaco2017 Jan]Promotion Counting(线段树合并)