【CF1917F】Construct Tree

题目

题目链接:https://codeforces.com/contest/1917/problem/F
给出 \(n\) 条边的边权,询问是否可以构造出一棵树,使得所有边都被用上恰好一次且直径为 \(d\)
\(n,d\leq 2000\)

思路

首先肯定是找出一条长度为 \(d\) 的链,然后判断可不可以把剩下的所有边都挂在这条链的带权重心上。
如果最长的两条边的和已经超过 \(d\) 了,那么无解。
否则,考虑最长的边是否在这个长度为 \(d\) 的链上:

  • 如果在,那就把最长的边放在链的最前端,然后将剩余的边都挂在链的第二个节点上。显然这样不会产生长度超过 \(d\) 的链。
  • 如果不在,那么需要找到这条链上是否存在一个点,使得这个点切开后两条新的链长度都不小于最长的边的长度。
    \(f[i][j][k]\) 表示是否存在一种情况,满足前 \(i\) 个边选择了若干个,使得第一条链长度为 \(j\),第二条链长度为 \(k\)。然后直接上 01 背包即可。压掉第一维然后用 bitset 可以优化到 \(O(\frac{nd^2}{64})\)

时间复杂度 \(O(\frac{nd^2}{64})\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=2010;
int T,n,d,a[N];

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&d);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		sort(a+1,a+1+n);
		if (a[n]+a[n-1]>d) { printf("No\n"); continue; }
		bitset<N> f[d+1]; f[0][0]=1;
		for (int i=1;i<n;i++)
			for (int j=d;j>=0;j--)
			{
				f[j]|=(f[j]<<a[i]);
				if (j>=a[i]) f[j]|=f[j-a[i]];
			}
		bool flag=f[d-a[n]][0];
		for (int i=a[n];i<=d-a[n];i++)
			flag|=f[i][d-i];
		printf("%s\n",(flag ? "Yes" : "No"));
	}
	return 0;
}
posted @ 2023-12-28 12:43  stoorz  阅读(27)  评论(0编辑  收藏  举报