2020CCPC秦皇島 k Kingdom’s Power
題意是說給定一顆以1為根的樹,根節點處有無數個人,每一秒只能派一個人移動到他的相鄰節點上,問最少需要多少秒才能使所有結點被訪問過。
- 類似一個樹形dp。
- 把每個結點的val,初始化為從根走到它的步數。
- 一個初步的想法是,要盡可能把走到葉子結點的人也利用起來,避免從根節點走浪費太多秒。
- 設想一種情況,當前深度為10的結點A存在2條子鏈,左邊的長度為3,右邊的長度為5,那么最優的走法是從當前結點走向左邊,再由左邊的葉子走向右邊的葉子。
- 注意從左葉子回溯到A的時候,對應的步數不能直接更新為A的val,因為它只能被利用一次。因此對於一個結點,我們要按子樹高度大小升序遍歷。
- 對於某個結點,按照以上策略,往回回溯給出的值與它的最深的葉子有關,所以子樹的高度應為它最高的子樹h+1。
空間復雜度O(N)
時間復雜度O(nlogn)
跟CJH大佬一起口胡搞出來的思路
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
int val[maxn];
int fa[maxn];
vector<pair<int,int> > son[maxn];//height,NO
void init(int n){
for(int i=1;i<=n;++i)
son[i].clear();
}
int geth(int rt){
if(son[rt].empty()) return 1;
for(int i=0;i<son[rt].size();++i){
son[rt][i].first=geth(son[rt][i].second);
}
sort(son[rt].begin(),son[rt].end());
return son[rt].back().first+1;
}
int dfs2(int rt,int d,int v){
val[rt]=v;
if(son[rt].empty()) return 1;
int t=v;
for(int i=0;i<son[rt].size();++i){
t=min(d,dfs2(son[rt][i].second,d+1,t+1));
}
return t+1;
}
int main(){
// freopen("in.txt","r",stdin);
// ios::sync_with_stdio(false);
int t,n;
scanf("%d",&t);
for(int ca=1;ca<=t;++ca){
scanf("%d",&n);
init(n);
for(int i=2;i<=n;++i){
scanf("%d",fa+i);
son[fa[i]].push_back({0,i});
}
geth(1);
dfs2(1,0,0);
ll ans=0;
for(int i=1;i<=n;++i)
if(son[i].empty())
ans+=val[i];
printf("Case #%d: %lld\n",ca,ans);
}
return 0;
}