Loading

[JSOI2013]哈利波特与死亡圣器

链接:https://vjudge.net/problem/HYSBZ-5174

题目描述:给定一棵树,可以派出\(t\)个人,每一个人每一次可以将一个点染色,求对于每一个\(k\)能在\(k\)次内将一个深度为\(k-1\)的节点的叶节点染色时\(t\)的最小值。

题解:再一次绕到坑里去了。

我们先考虑直接树型\(dp\),令\(dp_{i}\)表示\(i\)子树的最小代价。

我们发现了了这样转移会有一个问题,一个提前给子树染色会给子树多添一个人,但这个人会马上消失,但在\(dp\)中却无法表示它消失。

所以我们还需要知道多添的人的个数,令\(dp_{i,j}\)表示\(i\)的子树中,多添了\(j\)个不常住人口的最小代价。

但这样转移是\(O(n^2)\),但我们发现我们只需要\(dp_{i,j}<=ans\)就行了,所以我们可以二分答案,这样可以将\(dp_{i,j}\)答案这一维压掉。

\(dp_{i}\)表示\(i\)的子树中最少要添的人数。

假如你和我一样,再转移时将余下的人补成一样的数\(x\),再取最小的\(x\)的话,你会收获很多的\(wa\),这个坑还让我还去看了题解。

其实,\(x=0\),为什么?毕竟每一次一定要把子树染平,只有染平了下一次才不会该节点的考虑了。

#include<iostream>
#include<cstdio>
using namespace std;
struct node
{
	int v,nxt;
};
node edge[2000001];
int n,head[1000001],fa[1000001],son[1000001],dp[1000001],length,len;
bool used[1000001];
void add(int x,int y)
{
	edge[++len].v=y;
	edge[len].nxt=head[x];
	head[x]=len;
	return;
}
void dfs(int x,int L)
{
	son[x]=0; 
	used[x]=1;
	for (int i=head[x];i>0;i=edge[i].nxt)
		if (!used[edge[i].v])
		{
			fa[edge[i].v]=x;
			dfs(edge[i].v,L);
			son[x]++;
		}
	dp[x]=son[x]-L;
	for (int i=head[x];i>0;i=edge[i].nxt)
		if (fa[edge[i].v]==x)
			dp[x]+=dp[edge[i].v];
	dp[x]=max(dp[x],0);
	used[x]=0;
	return;
}
bool check(int L)
{
	dfs(1,L);
	return (dp[1]==0);
}
int main()
{
	int first,last,mid,ans=1e9,x,y;
	cin>>n;
	for (int i=1;i<=n-1;++i)
	{
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	first=0;
	last=n;
	while (first<=last)
	{
		mid=(first+last)/2;
		if (check(mid))
		{
			ans=min(ans,mid);
			last=mid-1;
		}
		else
			first=mid+1;
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2022-12-14 21:42  zhouhuanyi  阅读(40)  评论(0)    收藏  举报