2020.7.16 T4【NOIP2015模拟10.29B组】抓知了(jz暑假训练day2)

Description

深海龙王和水葫芦娃放了暑假闲的无聊,一天他们路过一棵树,听到树上的知了叫的好欢啊∼
深海龙王准备抓几只知了送给水葫芦娃。他发现面前的这棵树是一颗以1 号节点为根节点的一颗有根树,同时他又发现这颗树上的每一个节点i 上都恰好停有一只蝉,正在愉快的以ai 的响声鸣叫∼
深海龙王会从1 号节点起沿着树边一直爬,直到爬到一个叶子节点(请不要在意他怎么下来),在这途中他可以选择一些他经过的蝉并将它们抓起来。但是水葫芦娃希望深海龙王抓的知了能发出越来越响的鸣叫声,起码得要单调不减!

Input

第1 行包含一个整数n,表示树上的结点个数;
第2 行包含n − 1 个整数,分别代表第2 至n 号节点的父亲节点编号;
第3 行包含n 个整数ai,代表i 号节点知了的响声。

Output

一行一个整数,表示深海龙王最多能抓到的知了数。

Sample Input

11
1 1 1 2 2 6 7 3 3 10
6 5 2 2 6 4 3 2 10 2 3

Sample Output

3

呃呃,数据范围的话,n<=100000,不想把图搞上来了

赛时

T3打完就还剩20秒了,所以。。。。。。没做

正解

树上最长不下降子序列,那么只是让你求长度,就不需要n方了,有一种nlog(n)的算法,这个呢有个缺点就是不能打印,但是题目也没让打印,所以就这么做喽。
这个算法呢,先多一个数组b(不下降)来储存数,然后大致方法是这样的:
遇到一个数若>=b数组的最后一位,那么直接存进去。
若这个数<最后一位的话,那么就找到b中比它的大的第一个数(后继),将它替换。这个用二分就可以了。
不断重复上述过程,最后的长度就是答案,但是b里面的数是乱的(说白了就是没用)
那么为什么呢?因为虽然最后数不是对的,但是它们的相对位置是不变的,所以剩下的我就不知道了,证明的话最长不下降子序列nlogn算法就这样吧。
那么剩下的就是在树上搞了,其实一模一样的,注意要回溯,而且a[i]有可能是负数,b要初始化无穷小,不然95分,有个点死都过不了。

#include<cstdio>
#include<iostream>
#include<cstring>
#define N 100007
#define INF 0x3f3f3f3f
using namespace std;
int n,len,maxn,a[N],b[N];
int f[N],head[N<<3],cnt;
struct node{
	int to,nxt;
}e[N<<1];
void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void dfs(int x){
	if(b[len]<=a[x]){
		b[++len]=a[x];
		for(int i=head[x];i;i=e[i].nxt)
			if(e[i].to!=f[x])dfs(e[i].to);
		maxn=max(maxn,len);
		b[len]=0;
		len--;
	}else{
		int l=1,r=len,mid=0;
		while(l<r){
			mid=l+r>>1;
			if(b[mid]>a[x])
				r=mid;
			else l=mid+1;
		}
		int t=b[r];
		b[r]=a[x];
		for(int i=head[x];i;i=e[i].nxt)
			if(e[i].to!=f[x])dfs(e[i].to);
		maxn=max(maxn,len);
		b[r]=t;
	}	
}
int main(){
	freopen("cicada.in","r",stdin);
	freopen("cicada.out","w",stdout);
	memset(b,-INF,sizeof(b));
	scanf("%d",&n);
	int x;
	for(int i=2;i<=n;i++)
		scanf("%d",&x),add(x,i),add(i,x),f[i]=x;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	dfs(1);
	printf("%d",maxn);
}

没什么好说的吧???

猜你喜欢

转载自blog.csdn.net/jay_zai/article/details/107392738