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

相关文章

不删数据还能合并磁盘? 让电脑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函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后