洛谷P3523 [POI2011] DYN-Dynamite

前言

本来一个很直观的题面,非要搞形式化题意反而使题意变得非常迷惑。

题意

Link

有一栋树形建筑,其中有一些点摆放了炸弹,树边上都摆放了引信,引信的燃烧时间为 \(1\)\(/\) 边,现在你要选择 \(m\) 个点同时点燃引信(起爆),则显然炸弹被引爆的时间为到离它最近的起爆处的距离,请你求出所有炸弹爆炸所需的最短时间。

思路

首先最大值最小肯定是二分,check(mid) 就是求至少需要同时在多少点起爆才能做到 \(mid\) 秒内所有炸弹引爆,如果这个值大于 \(m\) 则说明时间不够,小于 \(m\) 说明时间还可以更短。当然 check 不只有这一种,但个人认为这是相对容易的方式。

那么可以考虑树形 dp(严格来讲是贪心),记 \(f[x][0]\)\(x\) 子树中离 \(x\)的还不能被引爆的炸弹距离,\(f[x][1]\)\(x\) 子树中离 \(x\)起爆点距离。

  • 从儿子转移到父亲是容易的,直接 \(f[u][0]=\max\{f[v][0\}+1\)\(f[u][1]=\min\{f[v][1]\}+1\)

  • 如果 \(u\) 自己就是炸弹,那么更新 \(f[u][0]\)

  • 如果 \(f[u][0]+f[u][1]\leq mid\),说明 \(u\) 子树中的某个起爆点可以在 \(mid\) 秒内,通过经过 \(u\) 的引信引爆 \(u\) 子树中最远的炸弹,那既然最远的都能引爆,说明 \(u\) 子树中所有的炸弹都能被引爆,于是 \(f[u][0]=-\infin\)

  • 如果离 \(u\) 最远的炸弹距离等于 \(mid\) 了,那么就必须在 \(u\) 设置起爆点,更新 \(f[u][0]\)\(f[u][1]\)

这样贪心一定是优的,因为我们每个炸弹都是拖到实在不能再拖的时候才起爆的。并且局部最优导致整体最优:子树内起爆点越靠“上”,则对于子树外的炸弹只会更有好处。

代码

#include<bits/stdc++.h>
#define getmax(x,y) x=max(x,y)
#define getmin(x,y) x=min(x,y)
#define getmid int mid=(l+r)/2
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar __getchar
inline char __getchar(){
    static char ch[1<<20],*l,*r;
    return (l==r&&(r=(l=ch)+fread(ch,1,1<<20,stdin),l==r))?EOF:*l++;
}
#endif
template<class T>inline void rd(T &x){
    T res=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while('0'<=ch && ch<='9'){res=res*10+ch-'0';ch=getchar();}
    x=res*f;
}
template<class T>inline void wt(T x,char endch='\0'){
    static char wtbuff[20];
    static int wtptr;
    if(x==0){
        putchar('0');
    }
    else{
        if(x<0){x=-x;putchar('-');}
        wtptr=0;
        while(x){wtbuff[wtptr++]=x%10+'0';x/=10;}
        while(wtptr--) putchar(wtbuff[wtptr]);
    }
    if(endch!='\0') putchar(endch);
}
const int MAXN=300005,INF=0x3f3f3f3f;
int n,m,f[MAXN][2],cnt,head[MAXN],ecnt=0;
bitset<MAXN>charge;
struct EDGE{
    int v,nxt;
}e[MAXN<<1];
inline void add(const int& u,const int& v){
    e[++ecnt].v=v;
    e[ecnt].nxt=head[u];
    head[u]=ecnt;
}
void dfs(int x,int fa,int val){
    f[x][0]=-INF,f[x][1]=INF;
    for(int i=head[x];i;i=e[i].nxt){
        int it=e[i].v;
        if(it==fa) continue;
        dfs(it,x,val);
        getmax(f[x][0],f[it][0]+1);
        getmin(f[x][1],f[it][1]+1);
    }
    if(charge[x]) getmax(f[x][0],0);
    if(f[x][0]+f[x][1]<=val) f[x][0]=-INF;
    if(f[x][0]==val){
        cnt++;
        f[x][0]=-INF;f[x][1]=0;
    }
}
inline bool check(int x){
    cnt=0;
    dfs(1,0,x);
    cnt+=(f[1][0]>=0);
    return cnt<=m;
}
int main(){
    rd(n);rd(m);
    for(int i=1,x;i<=n;i++){
        rd(x);
        charge[i]=x;
    }
    for(int i=1,u,v;i<n;i++){
        rd(u);rd(v);
        add(u,v);add(v,u);
    }
    int l=0,r=n,res=n-1;
    while(l<=r){
        getmid;
        if(check(mid)){
            res=mid;
            r=mid-1;
        }
        else{
            l=mid+1;
        }
    }
    cout<<res<<endl;
    return 0;
}
posted @ 2024-10-31 11:45  MessageBoxA  阅读(5)  评论(0编辑  收藏  举报