CF1863C MEX Repetition
思路
乍一看,感觉无从下手,于是就先列举了几个例子:
02
10
21
02
013
201
320
132
013
12345
01234
50123
45012
34501
23450
12345
容易发现周期是 \(n+1\),下面解释理由:
首先因为数量 \(n\),且两两各不相同,而值域是 \([0,n]\),所以在 \([0,n]\) 中,每个数最多存在一个,且有一个数是不存在的,我们假设是 \(p\)。
那么对于 \(a_1\),第一次一定变为 \(p\),于是这个数列中不存在的数就变成原来的 \(a_1\) 了,所以 \(a_2\) 就会变成原来的 \(a_1\),以此类推,第一次操作后,等于是把数组最后一位删去,整体往后挪一位,然后把第一位变成 \(p\)。
就这样不断地操作,经过 \(n\) 次,\(p\) 就会被挪到最后一位,然后下一次就会变为原样,所以周期是 \(n+1\)。
但是发现直接暴力还是会 TLE,所以做法肯定还要文雅些,既然都在之前找到规律了,我们就可以用上面的规律。
假设 \(k\) 是取模后的 \(k\),那么第 \(k\) 为一定是 \(p\),对于 \(k\) 前面的位置,应该就是原数组的后半段,比如,第 \(k-1\) 就是 \(a_n\),所以可以直接计算得到原数组的位置,对于 \(k\) 后面的位置,也可以同理推出对应的原数组的位置。
AC code
#include <bits/stdc++.h>
using namespace std;
int T,n,k,a[100005];
set<int>s;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k),k%=n+1;
for(int i=0;i<=n;++i) s.insert(i);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),s.erase(a[i]);
auto i=s.begin();int p=*i;
if(!k){for(int i=1;i<=n;++i) printf("%d ",a[i]);puts("");}
else
{
for(int i=1;i<=n;++i)
{
if(i<k) printf("%d ",a[n-k+i+1]);
else if(i==k) printf("%d ",p);
else printf("%d ",a[i-k]);
}
puts("");
}
s.erase(p);
}
return 0;
}