# 2021 ccpc post competition supplementary questions

## jumping monkey

General meaning:

There's one n n n-node tree, by n − 1 n-1 n − 1 edges are connected, and each node has a weight a i a_i ai, for any two nodes in the tree u u u， v v v. monkey can from u u u jump to v v v if and only if a v a_v av is from u u u to v v The shortest path of v (because it is a tree, there is only one path for any two nodes that does not pass through the double edge, and the shortest path is the path). Ask monkey from k ( k ∈ ( 1 , n ) ) k(k\in(1,n)) How many nodes can k(k ∈ (1,n)) pass at most.

Analysis: the initial idea was to find each node i i i is the smallest of all nodes that can jump - recorded as b [ i ] b[i] b[i], and then for the node i i i. Next, skip to b [ i ] b[i] b[i] is enough (yes) b [ i ] b[i] b[i], jump to b [ b [ i ] ] b[b[i]] b[b[i]], and so on). It ends when jumping to the last node, and the number of nodes passed is a n s ans ans (the essence of the actual final solution is the same), but the method of finding the nearest node uses a more complex algorithm, resulting in tl.
A better implementation is to establish a tree, i i The parent node of i is b [ i ] b[i] b[i], so that the depth of each node is ans (the depth of the root node is) 1 1 1) , we enumerate nodes from small to large by weight i i i. Enumerate the nodes smaller than him (enumerated), find the root node of the node (the weight of the enumerated node i must be greater than the original root node), and connect I.
However, the complexity of this implementation is still high (the problem lies in finding the root node. When the tree depth is large, there are many recursions to find the root node upward). At this time, I think of the way of using and querying sets. For nodes i i i. Set two properties r o o t [ i ] root[i] root[i] and f a [ i ] fa[i] fa[i], respectively refers to the root node of the tree and the direct parent node of the node. When finding the root node, the root nodes of all nodes along the way are updated, so that the complexity is greatly reduced when finding the root node next time a n s ans ans can be used a n s [ i ] = a n s [ f a [ i ] ] + 1 ans[i]=ans[fa[i]]+1 ans[i]=ans[fa[i]]+1. Finally, it should be noted that the ans of small nodes should be calculated from large to small according to the weight, because the ans of small nodes is recursively derived from large nodes.
ac Code:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 1e5 + 5;

vector<int> edge[N];
int a[N];
int order[N];//Order array records the order of weights from small to large
int fa[N];
int root[N];
int ans[N];

bool cmp(int x, int y)
{
return a[x] < a[y];
}

int getroot(int x)
{
if (root[x] == x)
{
return x;
}
int r = getroot(root[x]);
ans[x] = ans[fa[x]] + 1;
root[x] = r;
//cout << "ans" << x << "=" << ans[x] << endl;
return r;
}

#if 0
int solve(int i)
{
if (fa[i] == i)
{
ans[i] = 1;
return ans[i];
}
if (ans[i])
return ans[i];
ans[fa[i]] = solve(fa[i]);
ans[i] = ans[fa[i]] + 1;
return ans[i];
}
#endif

int main()
{
ios::sync_with_stdio(false), cin.tie(0);
int t;
cin >> t;
int n;
while (t--)
{
cin >> n;
for (int i = 1; i <= n; i++)
{
edge[i].clear();
order[i] = fa[i] = root[i] = i;
ans[i] = 0;
}
int u, v;
for (int i = 1; i <= n - 1; i++)
{
cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(order + 1, order + n + 1, cmp);
for (int i = 1; i <= n; i++)
{
int now = order[i];
for (auto v : edge[now])
{
if (a[v] < a[now])
{
int r = getroot(v);
fa[r] = now;
root[r] = now;
ans[r]++;
}
}
}
for (int i = n; i >= 1; i--)//Recursion by weight from large to small
getroot(order[i]);
for (int i = 1; i <= n; i++)
cout << ans[i] + 1 << '\n';
}
return 0;
}


Tags: Algorithm

Posted on Wed, 13 Oct 2021 20:33:57 -0400 by mpiaser