题目链接
思路:我们可以发现要想一棵n个节点的二叉树的深度和是有范围的,最大的就是一条链的情况,最小的就是树的结构是完全二叉树的时候,只要是在这个范围内的一定可以构造出来。那么怎么构造呢,我们首先构造最长的就是一条链,然后从最深的节点依次往前(每次往前深度就会减1),一直模拟这个过程,等于d的时候停止就好了。一开始不知道怎么模拟从后往前的过程,其实不用那么麻烦,我们用一个vector存一下i深度时存哪些节点就好了。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e3+10;
set<int>s[maxn];
vector<int>g[maxn];
int main()
{
int T,n,d,low,ans,flag,p[maxn],m,parent[maxn],minn;
scanf("%d",&T);
while(T--)
{
ll k=1;
minn=flag=0;
scanf("%d%d",&n,&d);
ans=n*(n-1)/2;
for(int i=0;i<31;++i) if((k<<i)>n) {low=i;break;}
low--;
for(int i=0;i<=low;++i) p[i]=(k<<i);
for(int i=0;i<low;++i) minn+=i*p[i];
minn+=low*(n-p[low]+1);
if(d>ans||d<minn)
{
puts("NO");continue;
}
d=ans-d;
for(int i=0;i<n;++i) s[i].clear(),g[i].clear();
for(int i=0;i<n;++i) s[i].insert(i+1);
for(int i=n-1;i>low;--i)
{
if(flag||d<=0) break;
for(int j=i-1;j>=0;--j)
{
if(j<=low&&s[j].size()==p[j]) {m=j;break;}
d--;
if(d<=0) {s[i].erase(i+1),s[j].insert(i+1);flag=1;break;}
}
if(d>0) s[i].erase(i+1),s[m+1].insert(i+1);
else break;
}
for(int i=0;i<n;++i)
{
for(auto it=s[i].begin();it!=s[i].end();it++)
g[i].push_back(*it);
}
for(int i=1;i<n;++i)
{
int cnt=0;
for(int j=0;j<g[i].size();++j)
{
parent[g[i][j]]=g[i-1][cnt];
if(j%2==1) cnt++;
}
}
puts("YES");
for(int i=2;i<=n;++i) printf("%d%s",parent[i],(i==n)?"\n":" ");
}
}