• 【文文殿下】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;
    }
    
  • 相关阅读:
    判断语句和循环语句2.2比较运算符
    判断语句和循环语句2.5 if中使用else
    判断语句和循环语句2.1 True、False
    判断语句和循环语句2.9while循环
    判断语句和循环语句2.7 if嵌套
    判断语句和循环语句2.8应用:猜拳游戏
    判断语句和循环语句逻辑运算符
    K8S 搭建 Prometheus (一) 部署 nodeexporter, prometheusserver
    使用Hive运行Job程序报GC错误
    离线数仓(四)
  • 原文地址:https://www.cnblogs.com/Syameimaru/p/10271730.html
Copyright © 2020-2023  润新知