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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM