bzoj 1217: [HNOI2003]消防局的设立

时间:2021-03-17 00:49:12

Description

2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地。起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地A到基地B至少要经过d条道路的话,我们称基地A到基地B的距离为d。由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过2的基地的火灾。你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。

Input

输入文件的第一行为n,表示火星上基地的数目。接下来的n-1行每行有一个正整数,其中文件第i行的正整数为a[i],表示从编号为i的基地到编号为a[i]的基地之间有一条道路,为了更加简洁的描述树状结构的基地群,有a[i]

Output

输出文件仅有一个正整数,表示至少要设立多少个消防局才有能力及时扑灭任何基地发生的火灾。

Sample Input

6

1

2

3

4

5

Sample Output

2

题解:SBT,但是写树形DP简直作死,光定义 \(f[i][1/2/3]\) 是不够的,需要 \(f[i][1/2/3/4/5]\) 然后就弃疗写贪心,贪心思路是从边界条件开始.首先要明白:对于深度最深的没有被覆盖的点,这个时候肯定不能依靠它的儿子了,只能在某个父亲新建一个消防站,所以可以感性却有理有据的发现,这个父亲肯定深度越小越好,这样覆盖的点就越多,所以我们就建在它的爸爸的爸爸上,这样显然正确.对于这个题,可以推广到距离为k,也是同样的做法

复杂度\(O(n)\)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=1005;
int head[N],num=0,to[N<<1],nxt[N<<1],n,dep[N];
void init(int x,int y){
nxt[++num]=head[x];to[num]=y;head[x]=num;
}
int fa[N];bool mark[N];
void dfs(int x){
int u;
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(dep[u])continue;
dep[u]=dep[x]+1;fa[u]=x;
dfs(u);
}
}
struct node{
int x,dep;
bool operator <(const node &pp)const{
return dep>pp.dep;
}
}a[N];
void updata(int x,int dep){
int u;
mark[x]=true;
if(dep==2)return ;
for(int i=head[x];i;i=nxt[i]){
u=to[i];
updata(u,dep+1);
}
}
void work()
{
int x;
scanf("%d",&n);
for(int i=2;i<=n;i++){
scanf("%d",&x);
init(x,i);init(i,x);
}
dep[1]=1;fa[1]=1;dfs(1);
for(int i=1;i<=n;i++)a[i].x=i,a[i].dep=dep[i];
sort(a+1,a+n+1);
int ans=0;
for(int i=1;i<=n;i++){
if(mark[a[i].x])continue;
int pa=fa[fa[a[i].x]];
mark[pa]=true;
updata(pa,0);
ans++;
}
printf("%d\n",ans);
} int main()
{
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
work();
return 0;
}