Sicily 1140. 国王的遗产

1140. 国王的遗产

Constraints

Time Limit: 1 secs, Memory Limit: 32 MB

Description

哈丁国的国王一生善于管理,勤于政务,在国家里聚积了大量的财富。但他众多的孩子都不争气,相互间时常勾心斗角,却没有一个真正能接受国王传位的人。为了避免将来某儿子一人独揽大权,又出于不能让权力过度分散的考虑,临终前,国王作了一个决定:

国王他将一生的财富打造出了一条很大的金块链,这条金块链的形状比较特别,它由n块大块的黄金组成,国王准备了n-1条链条,将某些相邻的两块大黄金用链条连接起来,最后构成一条连通的金块链。下图是国王构建的一条金块链:

国王对每块黄金编上号(从1到n),然后立下了遗嘱:

•   儿子们按照年龄大小顺序,在现存的金块链中获得遗产。

•   对于某个儿子,他可以在现存金块链中剪掉某条链条,获得不超过现有金块总数一半的那一部分。

•   某个儿子取得他那部分金块后,剩下的部分由他后面的弟弟们继续操作。

•   最后一个儿子获得剩下的那些金块,国王将保证每个儿子都能获得遗产。

儿子们都是贪婪的,他们都会选取使自己得到最多的金块的那条链条来剪,当然,有时选取的方案不是唯一的,但是儿子们都会选择使自己获得的“金块组编号”最小的那一条链来剪。“金块组编号”大小定义为:对于长度相同为L的两个有序数组A和B,A<B当且仅当存在一个整数i(0<i≤L),使得A[1]=B[1],…,A[i-1]=B[i-1]且A[i]<B[i]。

Input

输入数据第一行为一个整数n(1≤n≤30000)和一个整数k(1≤k≤100),分别表示金块的总数与国王儿子的数量。接下来n-1行,每行两个整数x和y,表示编号为x的金块与编号为y的金块用链条连接起来。

Output

输出数据只有一行,包含有k个整数,分别表示每个儿子获得金块的数量(由大儿子到最小的儿子)。

Sample Input

6 31 22 33 42 53 6

Sample Output

3 1 2样例说明:这里,大儿子取得金块组{1,2,5},二儿子取得金块组{4},三儿子取得金块组{3,6}。

Problem Source

ZSUACM Team Member

转自:http://blog.csdn.net/chinaczy/article/details/6271405

// Problem#: 1140
// Submission#: 3587451
// The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// URI: http://creativecommons.org/licenses/by-nc-sa/3.0/
// All Copyright reserved by Informatic Lab of Sun Yat-sen University
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAX = 30005;
int N,K;
int head[MAX],next[MAX*2],V[MAX*2],edge;
int prt[MAX],cnt[MAX],minID[MAX],ans[MAX];
bool del[MAX],vis[MAX];
void addEdge(int u,int v)
{
    V[edge] = v;
    next[edge] = head[u];
    head[u] = edge++;
}
void init(int x,int fa)//预处理以x为根的情况
{
    cnt[x] = 1;
    prt[x] = fa;
    minID[x] = x;
    for(int e = head[x];e != -1;e = next[e])
    {
        int y = V[e];
        if(fa == y || del[y])   continue;
        init(y,x);
        cnt[x] += cnt[y];
        minID[x] = min(minID[x],minID[y]);
    }
}
void dfs(int x,int fa,int flag)
{
    del[x] = 1;
    for(int e = head[x];e != -1;e = next[e])
    {
        int y = V[e];
        if(fa == y || del[y] || y == flag)  continue;
        dfs(y,x,flag);
    }
}
void solve()
{
    int flag,_max,_min;
    for(int i = 1;i < K;++i)
    {
        int root = 1;
        while(del[root])    root++;
        init(root,0);
        _max = 0;
        _min = MAX;
        for(int i = 1;i <= N;++i)
        {
            if(i == root ||del[i])  continue;
            int t1 = cnt[root] - cnt[i];
            int t2 = cnt[i];
            if(t1 == t2)
            {
                _max = t1;
                flag = i;
                _min = minID[root];
                break;
            }
            if(t1 > _max && t1*2 <= cnt[root])
            {
                flag = i;
                _max = t1;
                _min = minID[root];
            }
            if(t2 >= _max && t2*2 <= cnt[root])
            {
                if(t2 == _max && minID[i] < _min)
                {
                    _max = t2;
                    flag = i;
                    _min = minID[i];
                }
                else if(t2 > _max)
                {
                    _max = t2;
                    flag = i;
                    _min = minID[i];
                }
            }
        }
        if(cnt[flag] >= cnt[root] - cnt[flag])
        {
            ans[i] = cnt[root] - cnt[flag];
            dfs(root,0,flag);
        }
        else
        {
            ans[i] = cnt[flag];
            dfs(flag,prt[flag],prt[flag]);
        }
    }
    ans[K] = N;
    for(int i = 1;i < K;++i)
    {
        printf("%d ",ans[i]);
        ans[K] -= ans[i];
    }
    printf("%d\n",ans[K]);
}
int main()
{
    int u,v;
    memset(head,-1,sizeof(head));
    scanf("%d%d",&N,&K);
    for(int i = 1;i < N;++i)
    {
        scanf("%d%d",&u,&v);
        addEdge(u,v);
        addEdge(v,u);
    }
    solve();
}                                 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值