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

来源:互联网 发布:c语言short % 编辑:程序博客网 时间:2024/04/28 03:52

Description

夹克老爷逢三抽一之后,由于采用了新师爷的策略,乡民们叫苦不堪,开始组织起来暴力抗租。夹克老爷很愤怒,他决定派家丁常驻村中进行镇压。诺德县 有N(<=105)个村庄,编号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 longusing 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);}
1 0