【Atcoder - AGC004D】Teleporter

解决如何改变最少数量的传送门,使从任何城镇出发经过K次传送都能到达首都的问题。利用反证法和贪心策略优化解决方案。
摘要由CSDN通过智能技术生成


@Problem Statement@

There are N towns in Snuke Kingdom, conveniently numbered 1 through N. Town 1 is the capital.

Each town in the kingdom has a Teleporter, a facility that instantly transports a person to another place. The destination of the Teleporter of town i is town ai (1≤ai≤N). It is guaranteed that one can get to the capital from any town by using the Teleporters some number of times.

King Snuke loves the integer K. The selfish king wants to change the destination of the Teleporters so that the following holds:

Starting from any town, one will be at the capital after using the Teleporters exactly K times in total.
Find the minimum number of the Teleporters whose destinations need to be changed in order to satisfy the king’s desire.

Constraints
2≤N≤10^5
1≤ai≤N
One can get to the capital from any town by using the Teleporters some number of times.
1≤K≤10^9

Input
The input is given from Standard Input in the following format:
N K
a1 a2 … aN

Output
Print the minimum number of the Teleporters whose destinations need to be changed in order to satisfy King Snuke’s desire.

Sample Input 1
3 1
2 3 1
Sample Output 1
2
Change the destinations of the Teleporters to a=(1,1,1).

Sample Input 2
4 2
1 1 2 2
Sample Output 2
0
There is no need to change the destinations of the Teleporters, since the king’s desire is already satisfied.

Sample Input 3
8 2
4 1 2 3 1 2 3 4
Sample Output 3
3
For example, change the destinations of the Teleporters to a=(1,1,2,1,1,2,2,4).

@Translation@

N 个城市,城市 1 为首都。
每一个城市都有一个传送门,传送门通向 N 个城市中的某一个(可以是它自身)。
保证每一个城市都可以通过传送门到达首都。
现在让你修改最少的传送门的目的地,使得每一个城市经过恰好 K 次传送门后到达首都。求这个最少的数量。

@Solution@

首先可以使用反证法证明最后首都的传送门一定是通向自己。
否则假如它通向 i,则首都走 K 次即 i 走 (K-1) 次恰到达首都。然而 i 走 K 次也恰到达首都,又因 i ≠ 首都,所以不可能。

相当于问题是改最少的边,使得所有点的深度 <= K。
假如要更改结点 i 的传送门目的地,则这个目的地改为首都显然更优一些。
不然它的深度还会大些。

贪心地考虑,我们先考虑深度最大的结点,假如它的深度 > K,则对于它来说,一定会更改它的某一个祖先。显然这个祖先的深度越小,能够使更多的结点变为一定合法的状态,即更优秀。

实现上,假如要更改某一个结点 i 的传送门,则将 i 这棵子树内的所有点打上 tag。已经打过 tag 的直接回到父亲。这样每一个结点最多只会被访问两次,因此时间复杂度为 O(n)。

找某一个结点深度最小的,能使这个结点变合法的祖先,也直接暴力跳父亲即可,不需要倍增法。为什么呢?首先你肯定跳了 K 次,然后这 K 个点就会被打上 tag,就不会被重复访问了。这样均摊算下来也是 O(n) 的。

@Code@

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
struct edge{
	int to;
	edge *nxt;
}edges[MAXN + 5], *adj[MAXN + 5], *ecnt = &edges[0];
void addedge(int u, int v) {
	edge *p = (++ecnt);
	p->to = v, p->nxt = adj[u], adj[u] = p;
}
struct node{
	int ind; int dep;
}a[MAXN + 5];
bool operator < (node a, node b) {
	return a.dep < b.dep;
}
int N, K;
int fa[MAXN + 5], vis[MAXN + 5], rev[MAXN + 5];
void dfs(int rt) {
	for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
		a[p->to].dep = a[rt].dep + 1;
		dfs(p->to);
	}
}
void dfs2(int rt) {
	if( vis[rt] ) return ;
	vis[rt] = true;
	for(edge *p=adj[rt];p!=NULL;p=p->nxt)
		dfs2(p->to);
}
int main() {
	int ans = 0;
	scanf("%d%d", &N, &K);
	for(int i=1;i<=N;i++) {
		scanf("%d", &fa[i]);
		if( i != 1 ) addedge(fa[i], i);
		else if( fa[i] != 1 ) ans++;
		a[i].ind = i;
	}
	dfs(1);
	sort(a+1, a+N+1);
	for(int i=N;i>=1;i--) {
		if( a[i].dep <= K ) break;
		if( vis[a[i].ind] ) continue;
		ans++; int x = a[i].ind;
		for(int j=1;j<K;j++)
			x = fa[x];
		dfs2(x);
	}
	printf("%d\n", ans);
}

@End@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值