【文文殿下】CF1098C Construct a tree 题解

题解

挺水的一道题. Rating $ \color{orange} {2300}$ 以下送命题。

首先我们知道,所有子树大小之和就是节点个数加上从根到所有节点的路径长度之和。

他要求度数尽可能小,所以我们二分度数\(k\).显然,度数越小,子树和越大。

对于一个\(k\)叉树,他的子树大小之和为\(n+k^2+k^3+...+rem\)

我们通过二分得到最大的边界\(k\)

然后,此时我们的子树大小\(s\)是要小于等于规定的子树大小和的。

我们考虑扩大子树大小。

显然,我们让某些节点,往深度扩展将会扩大我们的子树大小。

我们记录每个深度的节点个数,已经每个节点的深度。

我们尝试从深度最深的节点开始往下扩展,直至子树大小达到规定大小。

Tips:n-1叉树的大小显然为2n-1 而一条链的大小为 n(n+1)/2 。如果k不在这个范围内,则无解。

具体实现非常简单。代码如下

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 1e6+10;
ll n,k,cnt[maxn],d[maxn];
bool check(int x) {
    ll i(2),j,t=1,num=k-n,dep=0;
    memset(d,0,sizeof d);
    memset(cnt,0,sizeof cnt);
    while(i<=n) {
        t*=x;++dep;
        for(j=1;j<=t&&i<=n;++i,++j) cnt[dep]++,d[i]=dep,num-=dep;
    }
    if(num<0) return false;
    j=n;
    while(num) {
        ++dep;
        if(cnt[d[j]]==1) --j;
        t = min(num,dep-d[j]);
        cnt[d[j]]--;
        d[j]+=t;
        cnt[d[j]]++;
        num-=t;
        --j;
    }
    return true;
}
int main() {
    cin>>n>>k;
    if(k<(1LL*2*n-1)||k>(1LL*n*(n+1)/2)) {
        puts("No");
        exit(0);
    }
    int l = 1, r = n;
    while(l<r) {
        int mid = (l+r)>>1;
        if(check(mid)) r=mid;
        else l = mid+1;
    }
    check(r);
    puts("Yes");
    int pos;
    d[pos=1]=0; sort(d+2,d+1+n); memset(cnt,0,sizeof cnt);
    for(int i = 2;i<=n;++i) {
        while(d[pos]!=d[i]-1||cnt[pos]==r) ++pos;
        cout<<pos<<' ';cnt[pos]++;
    }
    return 0;
}

转载于:https://www.cnblogs.com/Syameimaru/p/10271730.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值