【51 nod】1378 夹克老爷的愤怒

30 篇文章 0 订阅
18 篇文章 0 订阅

Description

夹克老爷逢三抽一之后,由于采用了新师爷的策略,乡民们叫苦不堪,开始组织起来暴力抗租。夹克老爷很愤怒,他决定派家丁常驻村中进行镇压。诺德县 有N( <=105 <script type="math/tex" id="MathJax-Element-2"><=10^5</script>)个村庄,编号0 至 N-1,这些村庄之间用N - 1条道路连接起来。家丁都是经过系统训练的暴力机器,每名家丁可以被派驻在一个村庄,并镇压当前村庄以及距离该村庄不超过K段道路的村庄。夹克老爷一贯奉行最小成本最大利润的原则,请问要实现对全部村庄的武力控制,夹克老爷需要派出最少多少名家丁?

Solution

显然我们发现一个家丁越靠近根节点越优。基于这个贪心思想,我们设出d[i]表示一个点还可以往上扩展多少。一个点若放了家丁则它的d[i]为2*m,一个点若d[i]为-1则必须要放。同时我们还要处理一个f[i]表示还没有放家丁的点距离当前点的最大距离。
对于每个节点i,我们处理出它的儿子最大的d-1和最小d-1的尽量大的f(多个相同的选f尽量大的)。倘若最小的d为0,我们要判断三种情况:
1、当选的f大于等于m时直接放一个家丁在i点。
2、当选的f比最大的d-1-m还小时就不用管。解释一下:因为当前的选的最大的d-1是有可能转折一下从而覆盖到f那一边的,所以就不用在i放点了。
3、现在考虑我们最迟要在那个点放了,所以我们比较一下是f大还是m-d+deep[x]大,m-d+deep[x]是指原来选的最大的d-1的最远点的距离。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=2*1e5+5;
int first[maxn],last[maxn],next[maxn],d[maxn],deep[maxn],f[maxn];
int n,i,t,j,k,l,x,y,m,ans,num;
void lian(int x,int y){
    last[++num]=y;next[num]=first[x];first[x]=num;
}
void dg(int x,int y){
    int t,k=1,l=0;f[x]=deep[x]=deep[y]+1;
    for (t=first[x];t;t=next[t]){
        if (last[t]==y) continue;
        dg(last[t],x);
        d[x]=max(d[last[t]]-1,d[x]);
        if (d[last[t]]-1<k) l=f[last[t]],k=d[last[t]]-1;
        else if (d[last[t]]-1==k) l=max(l,f[last[t]]);
        f[x]=max(f[x],f[last[t]]);
    }
    if (k<0){
        t=l-deep[x];
        if (t>=m) d[x]=2*m,ans++;
        else if (t<=d[x]-m) return;
        else{
            if (d[x]>=0)f[x]=max(t,m-d[x])+deep[x];
            else f[x]=l;d[x]=-1;
        }
    }
}
int main(){
    //freopen("data.in","r",stdin);
    scanf("%d%d",&n,&m);
    for (i=1;i<n;i++)
        scanf("%d%d",&x,&y),x++,y++,lian(x,y),lian(y,x);
    memset(d,255,sizeof(d));
    dg(1,0);
    if (d[1]<m) ans++;
    else{
        for (t=first[1];t;t=next[t]){
            if (d[last[t]]>d[1] && d[last[t]]<m){
                ans++;break;
            }
        }
    }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值