3523 [POI2011]DYN-Dynamite(二分)(贪心)(树形DP)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 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;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/83421644
今日推荐