洛谷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

相关文章

Python中合并列表(list)的六种方法小结

《Python中合并列表(list)的六种方法小结》本文主要介绍了Python中合并列表(list)的六种方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录一、直接用 + 合并列表二、用 extend() js方法三、用 zip() 函数交叉合并四、用

利用Python实现Excel文件智能合并工具

《利用Python实现Excel文件智能合并工具》有时候,我们需要将多个Excel文件按照特定顺序合并成一个文件,这样可以更方便地进行后续的数据处理和分析,下面我们看看如何使用Python实现Exce... 目录运行结果为什么需要这个工具技术实现工具的核心功能代码解析使用示例工具优化与扩展有时候,我们需要将

Python实现获取带合并单元格的表格数据

《Python实现获取带合并单元格的表格数据》由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,所以本文我们就来聊聊如何使用Python实现获取带合并单元格的表格数据吧... 由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,现将将封装成类,并通过调用list_exc

Python实现合并与拆分多个PDF文档中的指定页

《Python实现合并与拆分多个PDF文档中的指定页》这篇文章主要为大家详细介绍了如何使用Python实现将多个PDF文档中的指定页合并生成新的PDF以及拆分PDF,感兴趣的小伙伴可以参考一下... 安装所需要的库pip install PyPDF2 -i https://pypi.tuna.tsingh

使用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剪切视频合并视频转换视频结论介绍