问题:
某个点上的引线被点燃后的1单位时间内,在树上和它相邻的点的引线会被点燃。如果一个有炸 药的点的引信被点燃,那么这个点上的炸 药会爆炸。
求引爆所有炸 药的最短时间。
解:
树形DP+二分答案
设$f1[x]$表示距离x最远的未被点然的炸弹
$f2[x]$表示距离x最近的已经被点燃过的炸弹
$f1[x]=max(f1[x],f1[u]) $ u是x的儿子
$f2[x]=min(f2[x],f2[u]) $ u是x的儿子
后序遍历 三种情况
if(isbo[v]&&f2[v]>t) { f1[v]=max(0,f1[v]); //v是未盖点 取max } if(f1[v]+f2[v]<=t) {f1[v]=-10000000;} //能被盖住 if(f1[v]==t) {num++;f2[v]=0; f1[v]=-10000000;} //必须要点然 }
DP即可 注意判断根的情况
code:
#include<stdio.h> #include<cstdlib> #include<ctime> #include<algorithm> using namespace std; #define maxnn 4000000 #define inf 100000000 int isbo[maxnn]; int f[maxnn]; int f1[maxnn],f2[maxnn]; int n,m; int t; int cnt[maxnn],tr; int las[maxnn],nex[maxnn],en[maxnn],le[maxnn],tot; inline 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; } void add(int a,int b) { en[++tot]=b; nex[tot]=las[a]; las[a]=tot; } void dfs(int fa,int v) { f[v]=fa; for(int i=las[v];i;i=nex[i]) { int u=en[i]; if(u!=fa) { dfs(v,u); } } cnt[++tr]=v; } int bfs() { int num=0; for(int y=1;y<=tr;y++) { int v=cnt[y]; if(isbo[v]) f1[v]=0; else f1[v]=-100000000; f2[v]=inf; for(int i=las[v];i;i=nex[i]) { int u=en[i]; if(u!=f[v]) { f1[v]=max(f1[v],f1[u]+1); f2[v]=min(f2[v],f2[u]+1); } } if(isbo[v]&&f2[v]>t) { f1[v]=max(0,f1[v]); } if(f1[v]+f2[v]<=t) {f1[v]=-10000000;} if(f1[v]==t) {num++;f2[v]=0; f1[v]=-10000000;} } return num; } bool ch() { int tot=bfs(); if(f1[1]>=0) tot++; return tot<=m; } int main(){ n=read();m=read(); int s; int x,y,z; int l=0,r=n-1; for(int i=1;i<=n;i++) { isbo[i]=read(); } for(int i=1;i<n;i++) { x=read(); y=read(); add(x,y); add(y,x); } dfs(1,1); int mid; while(l<=r) { mid=(l+r)/2; t=mid; if(ch()) r=mid-1; else l=mid+1; } printf("%d",(l)); }