Jumping Monkey

Jumping Monkey

Link

题意

一个树,每个点有一个权重, u u u可以跳到 v v v当且仅当 v v v的权重是 u → v u \to v uv这条路径的最大值。问你从每一个点出发,最多可以经过多少个不同的点。

题解

假设点权最大的点为 u u u,那么不管从哪个点开始跳,最终一定会跳到这个点,因为 u u u是点权最大的点,所以到 u u u以后就停了。所以 u u u一定是每一个点出发的最优解的终点(对现有的所有的可达点的贡献都是1),我们将 u u u从树上去掉,并把相连的边去掉,对于剩下的每个联通块都递归做(因为这样做每一步都是最优的,所以到某一节点被取出,这个点就是最优解)。

发现这么递归算不好维护进行,因此反向考虑这个过程(做等价变换):按点权从小到大枚举结点 u u u,并将 u u u作为所有和 u u u相连的(已经枚举过的)连通块的根。建一棵新树,结点的深度就是res。

u u u作为所有和 u u u相连的(已经枚举过的)连通块的根,这样相当于他的子树中的每一个点都可以到 u u u这个点, u u u会使得每个点的深度加一(相当于可以这种跳法多到一个点,做了一个树上差分,最后可以得到res)。

可以暴力枚举每一个点的临点,是否是前面出现过的,如果是就找到那个子树的树根并连到当前这个点(用一个并查集维护),最后dfs求一下深度即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 4 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx, h1[N];
void add(int h[], int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], b[N];
int f[N];
int dep[N];
bool cmp(int u, int v) {
    return a[u] < a[v];
}
int find(int x) {
    return f[x] == x ? x : f[x] = find(f[x]);
}
void dfs(int u, int p) {
    dep[u] = p;
    for (int i = h1[u]; ~i; i = ne[i]) {
        int j = e[i];
        dfs(j, p + 1);
    }
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;     
        for (int i = 1; i <= n; i ++ ) h[i] = h1[i] = -1;
        idx = 0;
        for (int i = 0; i < n - 1; i ++ ) {
            int a, b;
            cin >> a >> b;
            add(h, a, b), add(h, b, a);
        }
        for (int i = 1; i <= n; i ++ ) cin >> a[i], b[i] = f[i] = i;
        sort(b + 1, b + n + 1, cmp);        
        for (int i = 1; i <= n; i ++ ) {        
            int j = b[i];       
            for (int k = h[j]; ~k; k = ne[k]) {
                int u = e[k];
                if (a[u] < a[j]) {
                    u = find(u);
                    f[u] = j;
                    add(h1, j, u);
                }                 
            }
        } 
        dfs(b[n], 1);
        for (int i = 1; i <= n; i ++ ) cout << dep[i] << endl;
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值