[POI2011] DYN-Dynamite

據說這道題有ON做法
好像是真的
但是沒人寫
我也不會
所以我就寫了正常的O(nlogn)O(nlogn)做法啦
顯然要二分一下對吧
然後我們怎麼判斷呢?
考慮樹形DP:
f[u]f[u]表示以uu爲根節點的子樹中,最遠的沒有被覆蓋到的關鍵節點的距離
g[u]g[u]表示以uu爲根節點的自述中,最近的一個選擇的點的距離
那麼轉移顯然就是
f[u]=max{f[v]+1}f[u]=\max\{f[v]+1\}
g[u]=min{g[v]+1}g[u]=\min\{g[v]+1\}
但是還有別的情況啊,要不然答案就是0了啊


情況1
f[u]+g[u]δ()f[u]+g[u]\leq\delta(二分的值)時,讓uu子樹中所有未匹配的節點連向g[u]g[u]那個點就可以,f[u]f[u]清零


情況2
f[u]=δf[u]=\delta時,需要選擇當前的點,f[u]f[u]清零,g[u]=0g[u]=0


情況3
當我們做到1的時候,如果還有沒覆蓋的,就要把1選上


那麼爲了便於處理,當f[u]f[u]清零的時候,我們讓f[u]=1f[u]=-1,當g[u]g[u]清零的時候(當下面還沒有選過的時候),我們讓g[u]=infg[u]=inf
然後還有一些細節,比如說二分的判斷之類的
我不會告訴你我在這上面調了一個小時因爲我對拍的暴力寫掛了

#include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=3e5+5;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n,m,ans,tot;
int head[N],cnt;
int f[N],g[N];
int col[N];

struct Edge{
    int to,next;
}e[N<<1];

void add(int x,int y){
    e[++cnt]=(Edge){y,head[x]},head[x]=cnt;
}

void dfs(int u,int fa,int delta){
    if(!col[u])f[u]=-1;
    else f[u]=0;
    g[u]=2e9;
    RepG(i,u){
        int v=e[i].to;
        if(v==fa)continue;
        dfs(v,u,delta);
        if(f[v]!=-1)f[u]=max(f[u],f[v]+1);
        if(g[v]!=2e9)g[u]=min(g[u],g[v]+1);
    }
    if(f[u]!=-1){
        if(g[u]==2e9){
            if(f[u]==delta){
                f[u]=-1;
                tot++;
                g[u]=0;
            }
        }
        else{
            if(g[u]+f[u]<=delta)f[u]=-1;
            else if(f[u]==delta){
                f[u]=-1;
                tot++;
                g[u]=0;
            }
        }
    }
}

bool check(int delta){
    tot=0;
    dfs(1,0,delta);
    if(f[1]!=-1)tot++;
    return tot<=m;
}

int main()
{
    memset(head,-1,sizeof(head));
    read(n),read(m);
    Rep(i,1,n)read(col[i]);
    Rep(i,1,n-1){
        int x,y;
        read(x),read(y);
        add(x,y),add(y,x);
    }
    int l=0,r=n;
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    // check(1);
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章