收集签名

题目链接

  • 虽然这道题看起来好像不太能DP的样子,但事实上的确是树形DP,我们考虑每条边怎样被覆盖——而不是被整条路径局限了思维
  • 我们依次用x的每个子树y更新x,在子树中枚举i,i>0时,|i|表示有多少个“超级技能”起点向外扩展,i<0时,|i|表示有多少个“超级技能”起点向内扩展,同时子树内有|i|个“超级技能”终点被处理
  • 那么这条边就要被技能覆盖k次,产生i*k的代价
  • 同时,除非i=-lefy,否则这条边依然要被正常地走一次,产生w(x,y)的代价
  • 原子树的代价为dp[x][s-i]
  • 子树内其他边的代价为dp[y][i],因为i>0时,要有|i|个未处理的“超级技能”起点,才能向外扩展;i<0时,要有|i|个未处理的“超级技能”终点,才能向内扩展
  • 通过将dp的下标扩展到负数,统一“起点”“终点”两种情况
  • 对于一棵子树,在子树内的代价已经被计算的前提下,我们并不关心起点/终点的具体位置,而只关心里面有多少个起点/终点
点击查看代码
#include <bits/stdc++.h>
using namespace std;
vector<int>a[4005],c[4005];
int n;
long long k;
int fa[4005],s[4005],h[4005];
bool b[4005];
void dfs(int n1)
{
	if(n1!=1&&a[n1].size()==1)
	{
		h[n1]=1;
	}
	else
	{
		h[n1]=0;
	}
	s[n1]=b[n1];
	for(int i=0;i<a[n1].size();i++)
	{
		if(a[n1][i]!=fa[n1])
		{
			fa[a[n1][i]]=n1;
			dfs(a[n1][i]);
			s[n1]+=s[a[n1][i]];
			h[n1]+=h[a[n1][i]];
		}
	}
}
long long f[4005][8005];
void dp(int n1)
{
	for(int j=-h[n1];j<=s[n1];j++)
	{
		f[n1][j+h[n1]]=-1;
	}
	if(n1!=1&&a[n1].size()==1)
	{
		f[n1][-1+h[n1]]=0;
	}
	f[n1][h[n1]]=0;
	if(b[n1])
	{
		f[n1][1+h[n1]]=0;
	}
	for(int i=0;i<a[n1].size();i++)
	{
		if(a[n1][i]!=fa[n1])
		{
			int y=a[n1][i];
			dp(y);
			for(int j=-h[n1];j<=s[n1];j++)
			{
				f[0][j+h[n1]]=f[n1][j+h[n1]];
				f[n1][j+h[n1]]=-1;
			}
			for(int l=-h[y];l<=s[y];l++)
			{
				if(f[y][l+h[y]]!=-1)
				{
					for(int j=-h[n1];j<=s[n1];j++)
					{
						if(j-l>=-h[n1]&&j-l<=s[n1]&&f[0][j-l+h[n1]]!=-1)
						{
							long long val=abs(l)*k+(l>-h[y])*c[n1][i]+f[0][j-l+h[n1]]+f[y][l+h[y]];
							if(f[n1][j+h[n1]]==-1)
							{
								f[n1][j+h[n1]]=val; 
							} 
							else
							{
								f[n1][j+h[n1]]=min(f[n1][j+h[n1]],val);
							}
						}
					}
				}
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>k;
		for(int i=1;i<=n;i++)
		{
			a[i].clear();
			c[i].clear();
			int opt;
			cin>>opt;
			b[i]=opt;
			fa[i]=0;
		}
		for(int i=1;i<n;i++)
		{
			int u,v,w;
			cin>>u>>v>>w;
			a[u].push_back(v);
			a[v].push_back(u);
			c[u].push_back(w);
			c[v].push_back(w);
		}
		dfs(1);
		dp(1);
		cout<<2*f[1][h[1]]<<endl;
	}
	return 0;
}
posted @ 2024-08-27 11:56  D06  阅读(7)  评论(0编辑  收藏  举报