题意:
某个点上的引线被点燃后的1单位时间内,在树上和它相邻的点的引线会被点燃。如果一个有炸药的点的引信被点燃,那么这个点上的炸药会爆炸。
求引爆所有炸药的最短时间。
题解:
二分+贪心。
关键是想到二分,然后就好做了。
变成已知覆盖范围,问至少要多少个点覆盖所有染色点。
显然贪心是对的,不到临界点不点燃。
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,d[300010];
struct node{
int y,next;
}a[600010];int len=0,last[300010];
void ins(int x,int y)
{
a[++len].y=y;
a[len].next=last[x];last[x]=len;
}
int num;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int f[300010],g[300010];
void dfs(int x,int fa,int t)
{
if(d[x]) f[x]=0;
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa) continue;
dfs(y,x,t);
if(f[y]!=-1) f[x]=max(f[x],f[y]+1);
if(g[y]!=-1) g[x]=max(g[x],g[y]-1);
}
if(f[x]<=g[x]) f[x]=-1;
if(f[x]==t) f[x]=-1,g[x]=t,num++;
if(x==1&&f[x]!=-1) num++;
}
bool check(int t)
{
num=0;
memset(f,-1,sizeof(f));
memset(g,-1,sizeof(g));
dfs(1,0,t);
return num<=m;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++) d[i]=read();
for(int i=1;i<n;i++)
{
int x,y;x=read();y=read();
ins(x,y);ins(y,x);
}
int l=0,r=n,ans;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
}