版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83421644
题意
在一棵形如树的图上有一些炸药点,在树上任意选择一些点为点火点,使得最晚爆炸的炸药最早(即炸药点到点火点的最大距离最小)。
特性
对于一个时间,显然刚好在这个时间到头时爆炸是最优的。
题解
二分+贪心+树形DP
题意转化一下,用一些点,扩张最小的长度覆盖最小的关键点。
看到最大值最小首先想到二分(其原因我是今天才明白,最大值可以无限变大,一定有一个最小点,小于这个点的解不合法,大于这个点的解不优秀,这个就是二分性在一个题目下的变形)。
check的时候用树形DP,设f1[x]为x子树内未覆盖点距离,f2[x]为x子树剩余可以覆盖长度。
再根据贪心,能不设尽量不设。
如果不设,那么要向f节点反馈一下最远的炸药点的距离。
设的话可以覆盖x以上一段距离以内的点。
如果最终所需数小于允许设的点火点数,return true。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=(1<<30)-1;
const int maxn=300010;
int n,m,mid;
int dyn[maxn];
struct E{int y,next;}e[maxn*2];int len=1,last[maxn];
void ins(int x,int y)
{
e[++len]=(E){y,last[x]};last[x]=len;
}
int tot;
int f1[maxn],f2[maxn];//f1未覆盖点距离,f2剩余可以覆盖长度
void dfs(int x,int fa)
{
f1[x]=dyn[x]?0:-inf;f2[x]=-inf;
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
if(y==fa) continue;
dfs(y,x);
f1[x]=max(f1[x],f1[y]+1);
f2[x]=max(f2[x],f2[y]-1);
}
if(f1[x]>f2[x])//子树内不能自己解决
{
if(f1[x]==mid)//贪心,必须放
{
tot++;
f1[x]=-inf;
f2[x]=mid;
}
}
else f1[x]=-inf;//自己解决掉
}
bool check()
{
tot=0;
dfs(1,0);
if(f1[1]>=0) tot++;
return tot<=m;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&dyn[i]);
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
int l=0,r=inf,ans=-1;
while(l<=r)
{
mid=l+r>>1;
if(check()) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}