列表

详情


NC25908. 跑路

描述

给出一棵 n 点树, 1 号点为根
有 l 个人从根出发,沿最短路径跑到各自的目的地
每个人都会将经过的点(含根和目的地)打上标记
你可以指定每一个人的目的地
你需要最大化打标记的点数
m 次询问,每次询问给出参数 d ,要求所有人的目的地到根的距离不超过 d
(两点间距离为两点间边数)

精简版
n 点树, m 次询问,每次给出参数 d ,选出 l 条一端为根的长度 的路径
使路径并内点数最大,求这个最大值

输入描述

第一行两个正整数 n, m
第二行 n-1 个正整数,其中第 i 个数 f_i 表示点 i+1 与 f_i 之间有一条边
(保证
第三行一个正整数 l
第四行 m 个非负整数,为每次询问的参数 d

输出描述

m 行,每行一个正整数
其中第 i 行为第 i 次询问对应的最大点数

示例1

输入:

6 3
1 1 3 3 3
2
0 1 2

输出:

1
3
4

示例2

输入:

9 2
1 1 2 4 4 5 6 3
2
3 4

输出:

6
7

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++11(clang++ 3.9) 解法, 执行用时: 1368ms, 内存消耗: 140784K, 提交时间: 2019-06-28 21:35:48

#include <cstdio>
#include <vector>

using std::vector;

const int MAXN=3000011;

inline void relax(int &p, int v){
	if(p<v)	p=v;
}

int N, M, L;
int F[MAXN];
int Dep[MAXN], Md[MAXN];
int Ms[MAXN];
int Ans[MAXN];
vector<int> Add[MAXN];

int Val[MAXN];

int main(){
	
	scanf("%d%d", &N, &M);
	
	for(int i=2;i<=N;++i)	scanf("%d", &F[i]);
	
	scanf("%d", &L);
	
	Dep[1]=1;
	for(int i=2;i<=N;++i)	Dep[i]=Dep[F[i]]+1;
	
	for(int i=1;i<=N;++i)	Md[i]=Dep[i];
	for(int i=N;i>=2;--i)
		relax(Md[F[i]], Md[i]);
	
	for(int i=N, f;i>=2;--i){
		f=F[i];
		if(Md[Ms[f]]<Md[i])	Ms[f]=i;
	}
	
	for(int i=N, f;i>=2;--i){
		f=F[i];
		if(Ms[f]==i)	continue;
		for(int k=Dep[f]+1;k<=Md[i];++k)
			Add[k].push_back(k-Dep[f]);
	}
	
	for(int i=1;i<=Md[1];++i)	Add[i].push_back(i);
	
	for(int i=1;i<=Md[1];++i){
		Ans[i]=Ans[i-1];
		for(int p=0, s=Add[i].size(), k;p<s;++p){
			k=Add[i][p];
			++Val[k];
			if(Val[k]<=L)	++Ans[i];
		}
	}
	
	for(int v;M--;){
		scanf("%d", &v);
		++v;if(v>Md[1])	v=Md[1];
		printf("%d\n", Ans[v]);
	}
	
	return 0;
}

/*
6 3
1 1 3 3 3
2
0
1
2

1
3
4

*/

上一题