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);
}