[ARC110F]Esoswap

题目

传送门 to AtCoder

思路

明显是构造题。但是构造方法毕竟是另一个人类想到的,同为灵长类动物,我不信你会搞出什么牛鬼蛇神出来!兔子出题就不归我管了

所以只需要按照一个正常人的思路去判断。显然我们如果真的对整个序列进行操作,那就乱套了;所以只有两个入手点:监视某个值的移动,或者 监视某个位置的值

对于第一条,我们有这样的想法:数字 1 1 1 是可以任意移动的。能不能让数字 2 2 2 也自由移动呢?可以,只要让 1 1 1 垫在它前面。于是 3 3 3 也可以移动, 4 4 4 也可以……

可是仔细一算: 2 2 2 移动一步,可能需要 1 1 1 转一整圈; 3 3 3 移动一步,也可能需要 2 2 2 转一整圈……叠加下去,最后 ( n − 1 ) (n-1) (n1) 移动一步的代价高达 n n − 1 n^{n-1} nn1 了!

所以第一个思路失败了。尝试一下第二个思路。我们会发现,如果对 x x x 位置进行操作,相当于把目前的 a x a_x ax 放在 ( a x + x ) (a_x+x) (ax+x) 位置上。显然每个 a x a_x ax 都有独属于自己的位置。那么,假如我们对 x x x 一直进行操作,就没有哪个数字会重复出现。一直操作下去,是不是就得到了一个圆排列呢?

事与愿违, a x = 0 a_x=0 ax=0 时就无法操作了。接下来又有了两个想法,其一是 在此基础上调整,其二是随机几个位置来进行操作。第二种方法暂时不要考虑吧,我们没理由相信它是正解;还是看看能不能调整。

很可惜的是,不同的 x x x 会改变 a x a_x ax 的 “专有座位”。那么究竟怎样才能尽可能的保持原有的结构呢?比如说原来已经形成了 0 , 1 , 2 , … , k 0,1,2,\dots,k 0,1,2,,k 这样的上升序列,从哪里开始可以保护好它呢?很显然就是从 1 1 1 开始。

但是手玩一下又发现,根本不太可能形成 0 , 1 , 2 , … , k 0,1,2,\dots,k 0,1,2,,k 的结构。从 0 0 0 的下一个位置开始操作,肯定是用 ( n − 1 ) (n-1) (n1) 将其交换过来的,所以会得到 n − 1 , 0 n{\rm-}1,0 n1,0 。然后又在 0 0 0 的下一个位置开始操作,此时想要换出 ( n − 1 ) (n-1) (n1),必须用 ( n − 2 ) (n-2) (n2),然后会得到 n − 2 , n − 1 , 0 n{\rm-}2,n{\rm-}1,0 n2,n1,0 。等下,我们是不是找到规律了?

进行这样的重复操作后,一定使得 0 0 0 前面的连续段变长。那么 n n n 次就会使它构成圆排列(因为第一次只是把 0 0 0 定位)。而此时 0 0 0 应该在开头,所以我们从 1 1 1 开始,操作到 ( n − 1 ) (n-1) (n1),最后操作 0 0 0

时间复杂度 O ( n 2 ) \mathcal O(n^2) O(n2),操作次数不超过 n 2 n^2 n2

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
    
    
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MAXN = 105;
int a[MAXN], n, ans[MAXN*MAXN], top;
void doit(int x){
    
    
	ans[++ top] = x, swap(a[x],a[(x+a[x])%n]);
}
int main(){
    
    
	n = readint();
	rep(i,0,n-1) a[i] = readint();
	rep(i,1,n) while(a[i%n]) doit(i%n);
	printf("%d\n",top);
	rep(i,1,top) printf("%d\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/121401177