您的位置:首页 > 其它

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

2020-07-23 15:45 239 查看

Ancient Distance

原题请看这里

题目描述:

作为CoffeeChickenCoffee ChickenCoffeeChicken的成员,ZYBZYBZYB是一个具有出色数据结构技能的男孩。
请考虑以下问题:给出具有N{N}N个顶点的根树。 顶点的编号从1{1}1到N{N}N,并且根始终是顶点1{1}1。 您最多可以分配K{K}K个关键顶点,以使所有顶点之间的最大“祖先距离”尽可能小。 将顶点x{x}x的“祖先距离”表示为:x{x}x与从x{x}x到根的路径上的第一个关键顶点之间的距离。 例如,如果树为111 −-− 222 −-− 333,关键顶点集为 {\{{ 2 }\}},则所有顶点的“祖先距离”为{\{{ + ∞\infty∞,000,111 }\}} ZYBZYBZYB然后加强了这个问题:请找到每个KKK ∈\in∈ {\{{ 111,222,…\dots…,NNN }\}} 的答案。 您可以接受ZYBZYBZYB的挑战吗?

输入描述:

输入包含多个测试用例。
对于每个测试用例,第一行包含一个整数NNN ((( 111 ≤\le≤ NNN ≤\le≤ 222 ×\times× 101010 5^{_5}5​),表示树中的顶点数。第二行中有N−1{N-1}N−1个整数, i{i}i整数fif_ifi​ ((( 111 ≤\le≤ fif_ifi​ ≤\le≤ iii ))) 表示fif_ifi​与i+1{i + 1}i+1之间存在一条边。它保证最多有N>1000{N> 1000}N>1000个测试用例,并且在所有测试用例中,N{N}N之和不会超过1.21.21.2 ×\times× 101010 6^{_6}6​

输出描述:

对于每个测试用例,输出 kkk 分别为 111,222,…,nnn 时答案的和

样例输入:

3
1 2
3
1 1

样例输出:

3
2

思路:

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

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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: