Up and Down the Tree
Problem Description
You are given a tree with n vertices; its root is vertex 1. Also there is a token, initially placed in the root. You can move the token to other vertices. Let’s assume current vertex of token is v, then you make any of the following two possible moves:
- move down to any leaf in subtree of v;
- if vertex v is a leaf, then move up to the parent no more than k times. In other words, if h(v) is the depth of vertex v (the depth of the root is 0), then you can move to vertex to such that to is an ancestor of v and h(v)−k≤h(to).
Consider that root is not a leaf (even if its degree is 1). Calculate the maximum number of different leaves you can visit during one sequence of moves.
Input
The first line contains two integers n and k (1≤k<n≤106) — the number of vertices in the tree and the restriction on moving up, respectively.
The second line contains n−1 integers p2,p3,…,pn, where pi is the parent of vertex i.
It is guaranteed that the input represents a valid tree, rooted at 1.
Output
Print one integer — the maximum possible number of different leaves you can visit.
Sample Input
7 1
1 1 3 3 4 4
Sample Output
4
题意
有一棵树,1号节点为根节点,有一个人,可以按照以下规则移动
- 从当前节点进入其子树下的任一叶节点,
- 如果当前是叶节点,可以向上走不超过k步
初始在根节点,问最多能到达多少个不同的叶节点
题解:
对于一个叶节点,假如处于当前节点,一定是尽可能的往上退的,因为那样才能有机会访问更多的叶节点。所以设数组num,num[i]代表从当前节点走能访问到的叶子节点并最高回到i号节点的叶子数量。对于每个叶节点,求出它按照规则走,能到达的最高的节点。为什么不直接说向上走k步,因为存在叶子节点较深,向上走几步,然后到一个深度较小的叶子节点,然后在向上退k步的情况。这样能到达比直接向上k步更靠近根节点的点。
对于num数组,深度优先进行bfs,求每个节点对应的num。首先将叶节点放入队列中,然后先访问深度较大的节点,如果可以继续向上走,则将当前节点的num加给其父节点, 同时每个节点维护走到当前节点最多能再向上走几步的信息。
最后再从根节点dfs一下,求向下的路径中最大的num和饥渴。
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 1000100;
const int mod = 772002;
struct node{
int u, dep;
node(){}
node(int a, int b):u(a), dep(b){}
bool operator <(node b)const
{
return dep < b.dep;
}
};
//num:最多能回到i号节点的叶子节点的数量
//b:当前节点最多能向上走几步
int ans, num[maxn], fa[maxn], b[maxn], dep[maxn], vis[maxn];
vector<int> g[maxn];
void bfs(int n, int k);
void dfs(int u, int de, int num1);
int main()
{
int n, k, i, j;
scanf("%d %d", &n, &k);
for(i=2;i<=n;i++){
scanf("%d", &fa[i]);
g[fa[i]].push_back(i);
}
//dfs求深度
dfs(1, 1, 0);
ans = 0;
//bfs求每个点最多能到几个叶子节点并回到当前节点
bfs(n, k);
//dfs求最优解
dfs(1, 1, num[1]);
printf("%d\n", ans);
return 0;
}
void dfs(int u, int de, int num1)
{
ans = max(num1, ans);
dep[u] = de;
for(int i=0;i<g[u].size();i++)
dfs(g[u][i], de+1, num1 + num[g[u][i]]);
}
void bfs(int n, int k)
{
int i, j;
memset(vis, 0, sizeof(vis));
priority_queue<node> que;
for(int i=1;i<=n;i++){
if(!g[i].size()){
que.push(node(i, dep[i]));
b[i] = k;
num[i] = 1;
}
}
//深度优先bfs
while(!que.empty())
{
node p = que.top();que.pop();
if(b[p.u] == 0 || p.u == 1 || vis[p.u])continue;
vis[p.u] = 1;
b[fa[p.u]] = max(b[p.u]-1, b[fa[p.u]]);
num[fa[p.u]] += num[p.u];
que.push(node(fa[p.u], dep[fa[p.u]]));
//为避免重复计数,将当前节点的数量清0
num[p.u] = 0;
}
}