[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;
}
本文来自博客园,作者:zhouhuanyi,转载请注明原文链接:https://www.cnblogs.com/zhouhuanyi/p/16983684.html