BZOJ 2525 Poi2011 Dynamite 二分答案+树形贪心

题目大意:给定一棵树,有一些点是关键点,要求选择不超过 m 个点,使得所有关键点到最近的选择的点距离最大值最小
二分答案,问题转化为:
给定一棵树,有一些点是关键点,要求选择最少的点使得每个关键点到选择的点的距离不超过 limit
然后我们贪心DFS一遍
对于以一个节点为根的子树,有三种状态:

0.这棵子树中存在一个选择的点,这个选择的点的贡献还能继续向上传递
1.这棵子树中存在一个未被覆盖的关键点,需要一些选择的点去覆盖他
2.这棵子树中既没有能继续向上传递的选择的点也不存在未覆盖的关键点

是不是少了一种状态?如果这棵子树中既存在能继续向上传递的选择的点又存在未被覆盖的关键节点呢?

这种状态可以被归进第二种状态中,因为我们需要一个子树外的节点被选择去覆盖这个未覆盖的关键点,那么如果子树内的选择节点还可以覆盖子树外的某个关键节点,那么子树外的选择节点一定也可以覆盖这个关键节点,子树内的选择节点的贡献失去了意义,因此可以被归为状态2

如果是状态0,记录这个点的贡献还能向上传递多少
如果是状态1,记录子树中离根节点最远的未被覆盖的关键点的距离

然后贪心就行了
时间复杂度O(nlogn)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 300300
using namespace std;
struct abcd{
    int to,next;
}table[M<<1];
int head[M],tot;
int n,m,cnt,a[M];
int f[M],sta[M];
/* 0-已经被覆盖 1-需要被覆盖 2-没有被覆盖,也不需要被覆盖 */
void Add(int x,int y)
{
    table[++tot].to=y;
    table[tot].next=head[x];
    head[x]=tot;
}
void Tree_DP(int x,int from,int limit)
{
    int i,nearest_fire=-1,farthest_dynamic=a[x]-1;
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=from)
            Tree_DP(table[i].to,x,limit);
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=from)
        {
            if(sta[table[i].to]==0)
                nearest_fire=max(nearest_fire,f[table[i].to]-1);
            else if(sta[table[i].to]==1)
                farthest_dynamic=max(farthest_dynamic,f[table[i].to]+1);
        }
    if(nearest_fire<farthest_dynamic)
    {
        if(farthest_dynamic==limit)
        {
            ++cnt;
            f[x]=limit;
            sta[x]=0;
        }
        else
        {
            f[x]=farthest_dynamic;
            sta[x]=1;
        }
    }
    else if(nearest_fire!=-1)
    {
        f[x]=nearest_fire;
        sta[x]=0;
    }
    else
    {
        f[x]=0;
        sta[x]=2;
    }
}
bool Judge(int x)
{
    cnt=0;
    Tree_DP(1,0,x);
    if(sta[1]==1)
        ++cnt;
    return cnt<=m;
}
int Bisection()
{
    int l=0,r=n;
    while(r-l>1)
    {
        int mid=l+r>>1;
        if( Judge(mid) )
            r=mid;
        else
            l=mid;
    }
    return Judge(l)?l:r;
}
int main()
{
    int i,x,y;
    cin>>n>>m;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        Add(x,y);Add(y,x);
    }
    cout<<Bisection()<<endl;
    return 0;
}

你可能感兴趣的:(贪心,树形DP,bzoj,二分答案,BZOJ2525)