题意:给定nnn和ddd,构造或判断无法构造一棵二叉树使得所有点的深度(定义为到根距离)之和为ddd。
n,d≤5000n,d\leq 5000n,d≤5000
显然可以算出有解的ddd的下界和上界,分别是完全二叉树和链的情况。下面会证明在这个范围内一定有解。
考虑增量,先构造出一条链,每次选一个叶结点接到深度小111的位置。
具体实现可以按编号暴力枚举叶结点,然后暴力枚举新位置的父亲(即深度小222且儿子数<2<2<2)。如果找不到,因为它的祖先都遍历过,感性理解,这个叶结点上面都放满了,以后不可能再用,打上标记,下次增量的时候不访问。
一棵二叉树是完全二叉树,当且仅当所有倒数第三深的点都已经有两个儿子。所以不是完全二叉树时可以按上面的方法增量。
复杂度看似是O(n(n2−d))O(n(n^2-d))O(n(n2−d)),但实际上ddd小于下界时可以直接输出NO
显然下界是O(nlogn)O(n\log n)O(nlogn)的,也就是说只需要考虑nlogn<dn\log n<dnlogn<d
变换得n<dlognn<\frac{d}{\log n}n<lognd
那么复杂度不劣于O((dlogn)3)O((\frac{d}{\log n})^3)O((lognd)3)
当d=5000d=5000d=5000,nnn最小只能到根号级别
加上算法复杂度不满,可以通过
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 5005
using namespace std;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
int fa[MAXN],cnt[MAXN],sum[MAXN],dep[MAXN],bad[MAXN];
int main()
{sum[1]=1;for (int i=2;i<MAXN;i++) sum[i]=i+sum[i/2]+sum[i-i/2-1];for (int i=1;i<MAXN;i++) sum[i]-=i;for (int T=read();T;T--){int n,d;n=read(),d=read();int cur=n*(n-1)/2;if (d<sum[n]||d>cur) {puts("NO");continue;}fa[1]=0;for (int i=2;i<=n;i++) fa[i]=i-1;for (int i=1;i<n;i++) cnt[i]=1;cnt[n]=0;for (int i=1;i<=n;i++) bad[i]=0,dep[i]=i-1;while (cur>d){int u;for (u=1;u<=n;u++)if (!cnt[u]&&!bad[u])break;bad[u]=1;for (int p=1;p<=n;p++)if (cnt[p]<2&&dep[u]-dep[p]==2){--cnt[fa[u]],fa[u]=p;dep[u]=dep[p]+1,++cnt[p];bad[u]=0,--cur;break;}}puts("YES");for (int i=2;i<=n;i++) printf("%d%c",fa[i]," \n"[i==n]);}return 0;
}