ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Jumping Monkey 2021CCPC网络赛重赛1011

2021-10-11 02:00:36  阅读:294  来源: 互联网

标签:连通 Monkey int 赛重赛 2021CCPC fa maxn 权值 节点


题目链接: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;
}

 

标签:连通,Monkey,int,赛重赛,2021CCPC,fa,maxn,权值,节点
来源: https://www.cnblogs.com/Remilia-Scarlet/p/15391568.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有