洛谷3523 [POI2011]DYN-Dynamite(二分)(貪心)(樹形DP)

題意

在一棵形如樹的圖上有一些炸藥點,在樹上任意選擇一些點爲點火點,使得最晚爆炸的炸藥最早(即炸藥點到點火點的最大距離最小)。

特性

對於一個時間,顯然剛好在這個時間到頭時爆炸是最優的。

題解

二分+貪心+樹形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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章