[bzoj 2525--Poi2011]Dynamite

124 篇文章 2 订阅
10 篇文章 0 订阅

Byteotian Cave的结构是一棵N个节点的树,其中某些点上面已经安置了炸药,现在需要点燃M个点上的引线引爆所有的炸药。
某个点上的引线被点燃后的1单位时间内,在树上和它相邻的点的引线会被点燃。如果一个有炸药的点的引信被点燃,那么这个点上的炸药会爆炸。
求引爆所有炸药的最短时间。

好题啊。这种题竟然是“边权相等就贪心,边权不等就dp”。。。
首先二分答案,然后问题就转化成一个点如果点燃它的引线就能覆盖与它距离<=mid的点,求用最少的点来覆盖所有炸药点。关键是边权都为1,所以可以贪心。
f[x]表示在x的子树内未被覆盖的与x距离最远的炸药点距离,g[x]表示在x的子树内与x距离最近的点燃点距离。
如果f[x]+g[x]<=mid,那么就说明x子树内就可以搞定。
如果f[x]==mid,那么就说明x一定要被点燃。
剩下的情况就可以留给祖先处理。
那最后注意一下细节就可以了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
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;
}
inline void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inline void pr1(int x){write(x),putchar(' ');}
inline void pr2(int x){write(x),puts("");}
struct node
{
    int x,y,next;
}a[600010];int len,last[300010];
bool v[300010];
inline void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int n,limit,w;
int f[300010],g[300010];
inline void solve(int x,int fa)
{
    g[x]=n,f[x]=0;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa)continue;
        solve(y,x);
        f[x]=max(f[x],f[y]+1);g[x]=min(g[x],g[y]+1);
    }if(v[x]==false && !f[x])f[x]=-1;
    if(f[x]+g[x]<=limit)f[x]=-1;
    else if(f[x]==limit || (x==1 && f[x]>=0)){w++;f[x]=-1,g[x]=0;}
}			
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    n=read();int m=read();
    for(int i=1;i<=n;i++)v[i]=read();
    for(int i=1;i<n;i++){int x=read(),y=read();ins(x,y),ins(y,x);}
    int l=0,r=n,ans;
    while(l<=r)
    {
    	int mid=(l+r)>>1;limit=mid,w=0;solve(1,0);
    	if(w<=m)ans=mid,r=mid-1;
    	else l=mid+1;
    }pr2(ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值