2020牛客暑期多校训练营第四场Ancient Distance

Ancient Distance

原题请看这里

题目描述:

作为 C o f f e e C h i c k e n Coffee Chicken 的成员, Z Y B ZYB 是一个具有出色数据结构技能的男孩。
请考虑以下问题:给出具有 N {N} 个顶点的根树。 顶点的编号从 1 {1} N {N} ,并且根始终是顶点 1 {1} 。 您最多可以分配 K {K} 个关键顶点,以使所有顶点之间的最大“祖先距离”尽可能小。 将顶点 x {x} 的“祖先距离”表示为: x {x} 与从 x {x} 到根的路径上的第一个关键顶点之间的距离。 例如,如果树为 1 1 - 2 2 - 3 3 ,关键顶点集为 { \{ 2 } \} ,则所有顶点的“祖先距离”为 { \{ + \infty 0 0 , 1 1 } \} Z Y B ZYB 然后加强了这个问题:请找到每个 K K \in { \{ 1 1 , 2 2 \dots N N } \} 的答案。 您可以接受 Z Y B ZYB 的挑战吗?

输入描述:

输入包含多个测试用例。
对于每个测试用例,第一行包含一个整数 N N 1 1 \le N N \le 2 2 × \times 10 10 5 ^{_5} ),表示树中的顶点数。第二行中有 N 1 {N-1} 个整数, i {i} 整数 f i f_i 1 1 \le f i f_i \le i i 表示 f i f_i i + 1 {i + 1} 之间存在一条边。它保证最多有 N > 1000 {N> 1000} 个测试用例,并且在所有测试用例中, N {N} 之和不会超过 1.2 1.2 × \times 10 10 6 ^{_6}

输出描述:

对于每个测试用例,输出 k k 分别为 1 1 , 2 2 ,…, n n 时答案的和

样例输入:

3
1 2
3
1 1

样例输出:

3
2

思路:

二分答案 + + 线段树维护
这道题一看到求最大值最小就想到二分
每次贪心查找最远的点,向上k个点放关键点,然后删左子树(注:“删除”指打标记,因为之后的计算原树还要用)直到所有的点都被删除时改变二分上下界,最后得出答案。
做法:
按顺序枚举 1 1 ~ k k ,由于需要寻找最大值,所以用线段树维护整棵树的dfs序。
枚举k × \times 每次查找 × \times 调和级数 × \times 二分
总时间复杂度: O O ( ( N N l o g n log_n l o g n log_n l o g n log_n ) )

AC Code

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e6+5;
int a[MAXN],dep[MAXN],d[MAXN],ans[MAXN],v[MAXN];
int n,cnt,maxn,maxm,mid;
vector<int> vec[MAXN];
void dfs(int x)
{
	d[++cnt]=x;
	for(int i=0;i<vec[x].size();i++)
	{
		dep[vec[x][i]]=dep[x]+1;
		dfs(vec[x][i]);
	}
}//dfs序
void build(int left,int right,int l,int r)
{
	if(left>right||l>r) return;
	if(l==r)
	{
		for(int i=left;i<=right;i++)
			ans[i]=l;
		return;
	}
	mid=(left+right)/2;
	ans[mid]=0; 
	for(int i=1;i<=n;i++)
		v[i]=dep[i];
	for(int i=n;i>=1;i--)
	{
		if(v[d[i]]==dep[d[i]]+mid||d[i]==1)
		{
			ans[mid]++;
			v[d[i]]=-1;
		}
		v[a[d[i]]]=max(v[a[d[i]]],v[d[i]]);
	}
	build(left,mid-1,ans[mid],r);
	build(mid+1,right,l,ans[mid]);
}//二分+线段树
int main()
{
	while(~scanf("%d",&n))
	{
		memset(ans,0,sizeof(ans));
		memset(dep,0,sizeof(dep));
		dep[1]=cnt=maxn=maxm=0;
		for(int i=1;i<=n;i++)
			vec[i].clear();
		for(int i=2;i<=n;i++)
		{
			scanf("%d",a+i);
			vec[a[i]].push_back(i);
		}
		dfs(1);
		for(int i=1;i<=n;i++)
			maxn=max(maxn,dep[i]);
		build(0,maxn,1,n);
		for(int i=1;i<=maxn;i++)
			maxm+=i*(ans[i-1]-ans[i]);
		printf("%d\n",maxm);
	}
}

猜你喜欢

转载自blog.csdn.net/s260127ljy/article/details/107535110