C/C++教程

Jumping Monkey 2021CCPC网络赛重赛1011

本文主要是介绍Jumping Monkey 2021CCPC网络赛重赛1011,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=7136

题意:

  给一颗n个点的树,每个点标记为1到n,每个点有自己的权值ai(保证不一样)。有只猴子可以从u点跳到v点,当且仅当u点到v点的最短路径上权值最大的点是v点。问猴子从k点开始(k∈【1.n】)最大可以跳多少个点。对于样例二,猴子在点1时,它可以跳 1 -> 3 -> 2 -> 4 共4个点。

 

思路:

  第一眼看过去就想到换根DP,推了半天不会转移,然后想着从最大值开始dfs维护单调栈,发现还是搞不了,太菜了QAQ  

  考虑最大权值的点,显然该连通块的每个点都可以跳到该点,该点对该连通块内所有点的贡献为1(所有点都可以跳到该点上)

  然后考虑次大权值的点,如果某个点到次大权值点的最短路径上有最大值,显然那个点无法到达次大值点,所以我们干脆把该连通块最大值点的所有连边都砍了,分成若干了连通块,每个连通块都有自己的最大值,这样就可以去求别的连通块对答案的贡献,不断重复这个过程就可以维护出每个点的最终答案。如图:(红色数字表示该点能跳多少步,黑色数字表示节点权值)

  

  这个递归过程不好实现,所以我们考虑反过来进行这个过程。

  按权值从小到大枚举点u,首先把点u单独放入一个连通块(区分一下没遍历到的点),然后遍历它的相邻节点,如果有相邻节点在另一个连通块内,那么说明另一个连通块是因为点u砍断了点u的出边而产生的(因为权值从小到大枚举,当前的权值比枚举过的权值大),把他们重新合成一个连通块,其中点u作为父节点,另一个连通块的根作为子节点,这就有点像并查集存在一个上下级关系。枚举完后,并查集就形成了一颗新树,每个节点的深度即为答案(根节点深度为1)。

  模拟过程:假设一开始的图是: 3 - 1 - 4 - 2

  从小到大枚举权值:

  第一个是1,相邻的点为3,4,都没有遍历过,所以1单独成一个连通块。

  

 

   第二个是2,相邻的点为4,都没有遍历过,所以2单独成一个连通块。

  

 

   第三个是3,相邻的点为1,说明1所在连通块是因为3而拆出来的,现在合回去。

  

 

   第四个是4,相邻的点为1,2,说明1,2的连通块是因为4拆出来的,连通0块1的根是3,连通块2的根是2,合起来。

  

   这颗就是新的树了,根据并查集的关系建出来的树。

  如果看着这颗树,按一开始的思路进行拆连通块统计贡献,就会发现,每在一个连通块上删一个最大值,就相当于在这颗树上删去那个点,然后这个点包含的子树贡献全部+1,和一开始的想法一样,删着删着就会发现节点深度就是我们维护的贡献,妙啊。

 

  反思:这道题删一个点形成多个连通块,而并查集维护的是连通块,所以应该往并查集这个方向想?有没有大佬指点下思路应该怎么往这个方向靠近啊....

 

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 7;
vector<int> E[maxn],tu[maxn];
int n,fa[maxn],dep[maxn];
struct node {
    int x,val;
}a[maxn];
bool cmp(node a,node b) {
    return a.val < b.val;
}
int fid(int x) {
    return x == fa[x] ? x : fa[x] = fid(fa[x]);
}
void dfs(int u,int fa) {
    dep[u] = dep[fa] + 1;
    for (auto v:tu[u]) {
        if(v == fa) continue;
        dfs(v,u);
    }
}
void solve() {
    int x,y;
    scanf("%d",&n);
    for (int i=1; i<=n; ++i) {
        E[i].clear();
        tu[i].clear();
        fa[i] = dep[i] = 0;
    }
    for (int i=1; i<=n-1; ++i) {
        scanf("%d%d",&x,&y);
        E[x].push_back(y);
        E[y].push_back(x);
    }
    for (int i=1; i<=n; ++i) {
        scanf("%d",&a[i].val);
        a[i].x = i;
    }
    sort(a+1,a+1+n,cmp);
    for (int i=1; i<=n; ++i) {
        fa[a[i].x] = a[i].x;
        for (auto v:E[a[i].x]) {
            if(fa[v]) {
                int V = fid(v);
                fa[V] = a[i].x; 
                tu[V].push_back(a[i].x);
                tu[a[i].x].push_back(V);
            }
        }
    }
    dfs(a[n].x,0);
    for (int i=1; i<=n; ++i) printf("%d\n",dep[i]);
}
int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        solve();
    }
    return 0;
}

 

这篇关于Jumping Monkey 2021CCPC网络赛重赛1011的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!