题意:
给出长度为 \(N\) 的排列,每次你可以选择两个位置 \(i\),\(j\) 并交换上边的数,前提是 \(j - i + 1\) 是质数。
要求在 \(5N\) 次操作内,把这个序列排号,输出具体排列的操作。
题解:
哥德巴赫猜想: 任意大于二的偶数,都可表示成两个素数之和。
那么我们每次放心的移动就好了!
那我们贪心的按照 \(1, 2, 3, 4……\) 的顺序来排,(就是让 \(1\) 到第一位置,\(2\) 到第二位置),哥德巴赫猜想保证了我们每次都可以移得动。
每次我们把一个数往前移动 \(x\) 距离时,每步先移动一个尽量大的质数距离即可。
然后由于 \(n\) 以内的质数的个数为 \(\pi(n) \sim \frac{n}{\ln(n)}\) 个,质数间的平均间距为 \(\ln(n)\),所以可以保证在 \(5N\) 次一定可以完成。
代码中预处理哪些数是质数,以及每个数比它小的最大质数是多少。
/*
Date:2021.8.21
Source:CF432C
konwledge:哥德巴赫猜想:任意大于二的偶数,都可表示成两个素数之和。
*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <map>
#define orz cout << "AK IOI"
using namespace std;
const int maxn = 1e5 + 10;
inline int read()
{
int f = 0, x = 0; char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
inline void print(int X)
{
if(X < 0) {X = ~(X - 1); putchar('-');}
if(X > 9) print(X / 10);
putchar(X % 10 + '0');
}
int n, a[maxn];
map<int, int> m;
int cnt, flag[maxn];
bool vis[maxn], prime[maxn];
vector<pair<int, int> > ans;
void init()
{
prime[1] = 1;
for(int i = 2; i <= maxn; i++)
{
if(!vis[i])
{
prime[i] = 1;
for(int j = i; j <= maxn; j += i) vis[j] = 1;
}
}
for(int i = 2; i <= maxn; i++)
if(prime[i]) flag[i] = i;
else flag[i] = flag[i - 1];
}
int main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n = read();
init();
for(int i = 1; i <= n; i++)
a[i] = read(), m[a[i]] = i;
for(int i = 1; i <= n; i++)
{
while(m[i] != i)
{
int t = m[i] - i + 1; //需要移动的距离
int step = m[i] - flag[t] + 1; //flag[t] 小于需要移动的距离的最大的质数
ans.push_back(make_pair(step, m[i]));
m[a[step]] = m[i]; //交换
swap(a[m[i]], a[step]);
m[i] = step;
}
}
printf("%d\n", ans.size());
for(int i = 0; i < ans.size(); i++)
printf("%d %d\n", ans[i].first, ans[i].second);
return 0;
}
感谢 https://lhn.blog.luogu.org/solution-cf432c