[agc004d]Teleporter

题意:

给你一个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 }
原文地址:https://www.cnblogs.com/dcdcbigbig/p/9494018.html