洛谷P3605 [USACO17JAN]Promotion Counting 线段树合并

2024-03-30 02:58

本文主要是介绍洛谷P3605 [USACO17JAN]Promotion Counting 线段树合并,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目描述

The cows have once again tried to form a startup company, failing to remember from past experience that cows make terrible managers!

The cows, conveniently numbered 1…N (1≤N≤100,000), organize the company as a tree, with cow 1 as the president (the root of the tree). Each cow except the president has a single manager (its “parent” in the tree). Each cow i has a distinct proficiency rating, p(i), which describes how good she is at her job. If cow i is an ancestor (e.g. a manager of a manager of a manager) of cow j, then we say j is a subordinate of i.

Unfortunately, the cows find that it is often the case that a manager has less proficiency than several of her subordinates, in which case the manager should consider promoting some of her subordinates. Your task is to help the cows figure out when this is happening. For each cow i in the company, please count the number of subordinates j where p(j) > p(i).

输入格式:
The first line of input contains N.

The next N lines of input contain the proficiency ratings p(1) … p(N) for the cows. Each is a distinct integer in the range 1…1,000,000,000.

The next N-1 lines describe the manager (parent) for cows 2…N. Recall that cow 1 has no manager, being the president.

输出格式:
Please print N lines of output. The ith line of output should tell the number of subordinates of cow ii with higher proficiency than cow ii.

题意:给定一棵树,求每个点子节点权值大于本身权值的数量。

思路

离散化+动态开点+权值线段树+线段树合并
首先将所有节点p的值离散化,建一棵权值线段树保存p的值(权值线段树中存在区间内值的数量)。
dfs搜到叶子节点后回溯,每次把孩子节点和父亲节点在线段树中合并(合并过程见下文)。
最后进行区间查询大于节点权值的数量。
此处线段树需用动态开点,并注意线段树大小要开到30倍左右。

线段树合并原理

线段树合并:把两个线段树节点合并为一个,保存的信息整合在一起。
此题中线段树中保存的信息是某区间内值的数量,所以整合时把两权值相加即可。
假设要将u,v合并,
若u或v为空,则返回另一节点。
否则建一新节点t,整合u,v信息(此题中为两权值相加),再递归合并u,v的子树。
合并时需注意维护权值信息。

代码

#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
#define fast static int
using namespace std;
typedef long long ll;
int read()
{int x=0,f=1;char ch=getchar();while(ch<'0' || ch>'9'){if(ch=='-')	f=-1;ch=getchar();}while(ch>='0' && ch<='9'){x=10*x+ch-'0';ch=getchar();}return x*f;
}
const int Size=100005;
int n,p[Size],np[Size];
/*p:p的值,经离散化后会改变		np:离散化中排序用的数组*/
struct Edge
{int v,next;
} w[Size<<1];			//存边 
int cnt,head[Size];		//链式前向星 
inline void AddEdge(int u,int v)
{w[++cnt].v=v;w[cnt].next=head[u];head[u]=cnt;
}
inline int search(int x)		//二分查找np中x的位置(离散化用) 
{re l=1,r=n,mid;while(l<=r){mid=(l+r)>>1;if(np[mid]==x){return mid;} else if(np[mid]<x) {l=mid+1;} else {r=mid-1;}}return l-1;
}
int root[Size],tot,ans[Size];	//root[x]表示x在线段树中位置 //tot表示当前线段树中节点总个数 //ans记录答案 
struct node {int l,r,v;		//当前区间和权值 int lc,rc;		//左孩子和右孩子 
} tree[Size*30];
inline void Pushup(int rt)
{tree[rt].v=tree[tree[rt].lc].v+tree[tree[rt].rc].v;
}
void Insert(int l,int r,int x,int &rt)		//在线段树中插入节点x 
{rt=++tot;tree[rt].l=l;tree[rt].r=r;if(l==r){tree[rt].v=1;return;}int mid=(l+r)>>1;if(x<=mid)	Insert(l,mid,x,tree[rt].lc);if(x>mid)	Insert(mid+1,r,x,tree[rt].rc);Pushup(rt);
}
int Query(int l,int r,int rt)			//查询权值在区间[l,r]内的p的个数 
{
//	printf("%d %d %d\n",l,r,rt);if(!rt)	return 0;if(l<=tree[rt].l && tree[rt].r<=r){return tree[rt].v;}int ans=0,mid=(tree[rt].l+tree[rt].r)>>1;if(l<=mid)	ans+=Query(l,r,tree[rt].lc); if(r>mid)	ans+=Query(l,r,tree[rt].rc);return ans;
}
int merge(int x,int y)			//合并x,y 
{if(x==0 || y==0)	return x|y;if(tree[x].l==tree[x].r){tree[x].v+=tree[y].v;return x;}tree[x].lc=merge(tree[x].lc,tree[y].lc);tree[x].rc=merge(tree[x].rc,tree[y].rc);Pushup(x);return x;
}
void dfs(int x)					//dfs 
{for(int i=head[x]; i; i=w[i].next){dfs(w[i].v);root[x]=merge(root[x],root[w[i].v]);}ans[x]=Query(p[x]+1,n+1,root[x]);
}
void init()
{n=read();for(re i=1; i<=n; i++){p[i]=read();}for(re i=2; i<=n; i++){int x=read();AddEdge(x,i);}/*离散化*/for(re i=1; i<=n; i++){np[i]=p[i];}n=unique(np+1,np+1+n)-(np+1);sort(np+1,np+1+n);for(re i=1; i<=n; i++){p[i]=search(p[i]);Insert(1,n+1,p[i],root[i]);}
}
int main()
{init();dfs(1);for(re i=1; i<=n; i++){printf("%d\n",ans[i]);}return 0;
}

这篇关于洛谷P3605 [USACO17JAN]Promotion Counting 线段树合并的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/860319

相关文章

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://

使用Python创建一个能够筛选文件的PDF合并工具

《使用Python创建一个能够筛选文件的PDF合并工具》这篇文章主要为大家详细介绍了如何使用Python创建一个能够筛选文件的PDF合并工具,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录背景主要功能全部代码代码解析1. 初始化 wx.Frame 窗口2. 创建工具栏3. 创建布局和界面控件4

Python自动化办公之合并多个Excel

《Python自动化办公之合并多个Excel》在日常的办公自动化工作中,尤其是处理大量数据时,合并多个Excel表格是一个常见且繁琐的任务,下面小编就来为大家介绍一下如何使用Python轻松实现合... 目录为什么选择 python 自动化目标使用 Python 合并多个 Excel 文件安装所需库示例代码

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

基于C#实现PDF文件合并工具

《基于C#实现PDF文件合并工具》这篇文章主要为大家详细介绍了如何基于C#实现一个简单的PDF文件合并工具,文中的示例代码简洁易懂,有需要的小伙伴可以跟随小编一起学习一下... 界面主要用于发票PDF文件的合并。经常出差要报销的很有用。代码using System;using System.Col

Python视频剪辑合并操作的实现示例

《Python视频剪辑合并操作的实现示例》很多人在创作视频时都需要进行剪辑,本文主要介绍了Python视频剪辑合并操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录介绍安装FFmpegWindowsMACOS安装MoviePy剪切视频合并视频转换视频结论介绍

不删数据还能合并磁盘? 让电脑C盘D盘合并并保留数据的技巧

《不删数据还能合并磁盘?让电脑C盘D盘合并并保留数据的技巧》在Windows操作系统中,合并C盘和D盘是一个相对复杂的任务,尤其是当你不希望删除其中的数据时,幸运的是,有几种方法可以实现这一目标且在... 在电脑生产时,制造商常为C盘分配较小的磁盘空间,以确保软件在运行过程中不会出现磁盘空间不足的问题。但在

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,