Cyclic Operations 题解

发布时间 2023-12-19 12:14:48作者: Creeper_l

前言

看这道题有好多巨佬都是用 Tarjan 来做的,在这里讲一个自认为比较简单的做法,(不到 \(30\) 行)

题意

题意比较难讲,建议自己去看一下翻译,在这里不多赘述。

思路

首先看到题目中间给的一个每一次操作的式子:\(a_{l_{i}}=l_{(i\mod k)+1}\)。仔细观察这个式子后会发现,其实每一次操作的目的其实就是将 \(a_{i}\) 变为 \((i+1)\mod k\)。再看样例的数组从 \(1,2,3\) 变成了 \(2,3,1\),于是我们就联想到了环。所以我们可以从 \(i\)\(a_{i}\) 连一条边,这样就构成了一张图,且图中有若干个环。而每一个环其实就对应着每一次操作,不在环上的点不用考虑,因为随便选一些不在环上的点和一些在环上的点一共凑齐 \(k\) 个点之后按要求进行操作就可以达到目标了。

接下来只需要考虑每一个环是否符合要求就行了。因为题目上说了每一次操作选的数的数量必须刚好是 \(k\) 个,而每一个环上的点又必须一起选,所以我们只需要判断每一个环上的点数是否刚刚好等于 \(k\) 就行了。

注意当 \(k=1\) 的情况时需要特判一下。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair <int,int> pii;
const int MAXN = 2e5 + 10;
int T,n,k,a[MAXN],num[MAXN],vis[MAXN];
bool flag;
signed main()
{
	cin >> T;
	while(T--)
	{
		flag = true;
		cin >> n >> k;
		for(int i = 1;i <= n;i++) cin >> a[i],flag &= (!(k == 1 && a[i] != i));
		for(int i = 1;i <= n;i++)
		{
			if(vis[i] == true) continue;
			int u = i,cnt = 0;
			while(vis[u] == false) vis[u] = i,num[u] = ++cnt,u = a[u];
			if(vis[u] != i) continue;
			if(cnt - num[u] + 1 != k) flag = false;
		}
		if(flag == false) puts("No");
		else puts("Yes");
		for(int i = 1;i <= n;i++) num[i] = vis[i] = 0;
	}
	return 0;
}