2020 China Collegiate Programming Contest Qinhuangdao Site(K题)

题目:K. Kingdom’s Power
题意:根节点1上有无数个军队,每一次国王都可以指挥一支军队向相邻一个节点运动,问多少次占领所有的节点。

首先可以分析出在当前节点有一支军队,那么最小的走法,仔细考虑,必然是先走的子节点深度最浅的,由于走完最浅的返回路径不需要增加过多,但是根节点上有无数个军队,需要判断就是从根节点派兵还是让走到叶子节点的兵在返回。至此可以得出贪心的策略。
写法的话,就是sort当前节点的子节点按照最深树的高度排序,找是由其他树的节点走过来的还是根节点指派,一旦是根节点指派那么往上回溯的节点一定都是根节点指派的,不然就必须更新当前的可利用的节点深度。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
//#include<unordered_map>
#include<map>
#include<algorithm>
#include<queue>
#define mmp make_pair
#define inf 0x3f3f3f3f
#define llinf 0x7fffffffffffffff
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
typedef double ld;
vector<int>hh[1000100];
int high[1000100];
ll ans=0;
ll dp[1000100];
struct cmp1{
	bool operator()(PP x,PP y)
	{
		return x.first>y.first;//小的优先级高 ,从小到大排
	}
};
priority_queue <PP,vector<PP>,cmp1 > que[1000100];
void dfshigh(int u,int f) {
    high[u]=high[f]+1;
    dp[u]=0;
    if(hh[u].size()==1 && hh[u][0]==f) {
        return ;
    }
    for(int i=0;i<hh[u].size();++i) {
        int v=hh[u][i];
        if(v==f) continue;
        dfshigh(v,u);
        dp[u]=max(dp[u],dp[v]+1);
        que[u].push(mmp(dp[v],v));
    }
}
int dfs(int u,int f,int step) {
    while(!que[u].empty()) {
        PP temp=que[u].top();
        que[u].pop();
        step=dfs(temp.second,u,step+1)+1;
    }
    if(hh[u].size()==1 && hh[u][0]==f ) {
        ans+=min(high[u],step);
        return 0;
    }
    return step;
}
int main() {
    int T;
    scanf("%d",&T);
    int cases=1;
    while(T--) {
        int n;
        scanf("%d",&n);
        for(int i=0;i<=n;++i) {
            while(!que[i].empty()) {
                que[i].pop();
            }
            high[i]=dp[i]=0;
            hh[i].clear();
        }
        for(int i=2;i<=n;++i) {
            int x; scanf("%d",&x);
            hh[i].push_back(x);
            hh[x].push_back(i);
        }
        high[0]=-1;
        dfshigh(1,0);
        ans=0;
        dfs(1,0,0);
        printf("Case #%d: %lld\n",cases++,ans);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值