题意:
给你一个n个点n条边的基环外向树(即在一棵树上加了一条边,边有方向,且加的那条边一定有个端点是1号点),每条边长度为1,让你改变若干条边使得每个结点走k步均恰好可以到达1号点,求最小要改变几次。
$n<10^5,k<10^9$
题解:
本质是道简单题……但是题目描述非常鬼畜,导致我看了十分钟才看懂要干什么……
首先显然要把1号点那条多出来的边连回自己形成自环,否则$k>1$时肯定无法实现。然后就可以直接贪心了。。。从下到上计算距离,只要最深的结点距离超过了k就操作一次并把距离重设为0,然后就。。。没了。。。
注意如果1号点原本没有连自己答案要+1(因为自己连回自己要操作一次),很多人被这个坑了
代码:
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 #include<cmath>
5 using namespace std;
6 struct edge{
7 int v,next;
8 }a[100001];
9 int n,k,x,ans=0,tot=0,num[100001],head[100001],dis[100001];
10 void add(int u,int v){
11 a[++tot].v=v;
12 a[tot].next=head[u];
13 head[u]=tot;
14 }
15 void dfs(int u){
16 dis[u]=1;//开始忘了这句,只有50
17 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
18 int v=a[tmp].v;
19 dfs(v);
20 dis[u]=max(dis[u],dis[v]+1);
21 }
22 if(dis[u]>=k&&num[u]!=1){
23 ans++;
24 dis[u]=0;
25 }
26 }
27 int main(){
28 memset(head,-1,sizeof(head));
29 memset(dis,0,sizeof(dis));
30 scanf("%d%d",&n,&k);
31 for(int i=1;i<=n;i++){
32 scanf("%d",&num[i]);
33 if(i==1){
34 if(num[i]!=1)ans++;
35 num[1]=1;
36 }else{
37 add(num[i],i);
38 }
39 }
40 dfs(1);
41 printf("%d",ans);
42 return 0;
43 }