【51 Nod1378】夹克老爷的愤怒

来源:互联网 发布:业务流程数据化的例子 编辑:程序博客网 时间:2024/06/09 20:22

Description

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

Solution

这是一道很经典的题目了。
这种题可以用贪心来做,就像序列一样,从小往上,隔2k个放一个家丁,只用考虑一下当前的覆盖情况就可以了。
不过覆盖情况的考虑比较麻烦。
我们设g[i]表示当前i向上最大控制的距离。
我们现在考虑向上扩展最大的那个可不可以影响所有儿子,如果可以影响,那么最大的那个儿子肯定最又,否则就必须要用最小的那个儿子的值才可以覆盖所有的儿子。
我们对于i的儿子求一个最大值da和一个最小值xiao。
如果是叶子节点的话,那么g肯定是k-1的,因为还有k-1个才能到上面的家丁。
如果当前的g等于0的话,那么当前的i就是家丁的位置,那么此时ans++,然后g等于2*k,因为根据贪心,两个家丁的位置差2*k个。
我们现在要考虑一个儿子向上的家丁可不可以覆盖其他儿子,那么能覆盖最多的肯定是da,因为da是i离上面的家丁最远的位置,那么就是离下面的家丁最近的,那么i距下面的家丁(deep[i]-da+2*m)-deep[i]=2*m-da,距xiao的下面最远的位置(deep[i]-xiao+m)-deep[i]-1=m-xiao-1,所以要满足da+xiao>2*k就能用da来更新,否则要用xiao。

Code

#include<iostream>#include<stdio.h>#include<math.h>#include<string.h>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define rep(i,a) for(i=first[a];i;i=next[i])const int maxn=1e5+7,ju=0x7fffffff;using namespace std;int i,j,k,l,t,n,m,ans,f[maxn],g[maxn];int first[maxn*2],next[maxn*2],last[maxn*2],num;void add(int x,int y){    last[++num]=y,next[num]=first[x],first[x]=num;}void dfs(int x,int y){    int i;bool az=0;    int da=-ju,xiao=ju;    rep(i,x){        if(last[i]!=y){           dfs(last[i],x);           da=max(da,g[last[i]]),xiao=min(xiao,g[last[i]]);        }    }    if(xiao==ju)g[x]=k-1;    else if(xiao==0)g[x]=2*k,ans++;    else if(da+xiao>2*k)g[x]=da-1;    else g[x]=xiao-1;}int main(){    scanf("%d%d",&n,&k);    fo(i,1,n-1){        scanf("%d%d",&l,&t);l++,t++;        add(l,t),add(t,l);    }    dfs(1,0);    if(g[1]<k)ans++;    if(!k)ans=n;    printf("%d\n",ans);}
1 0