SEU寒假训练题解二 G Codeforces 432C

题意

给你1到n的一个排列,让你通过把将数字两两交换,把数列恢复升序。

要求交换的两数的下标i,j满足j-i+1为素数,且交换次数不超过5n。









题解

挺有意思的一道题。

首先不考虑素数的限制,要交换多少次才能实现目标呢。




答案是n次

方法是yy一下每个元素只有一个的计数排序,

从1到n,每次将元素i复位,占在i位置上的元素呢,丢到i原来在的地方就好啦。


那么现在考虑素数的限制。5次可以实现一次交换把一个元素复位嘛。显然是可以的。





答案是哥德巴赫猜想。

预处理好范围内偶数如何拆成两个素数(反正小范围内哥猜一定是对的,直接线性筛完素数后,n^2循环各种奇素数就可以得到了。而且还一定不会重复,我觉得效率挺好。)

可以通过pp[p1+p2]=p1来储存。

剩下就很简单了,先判素数,不是的话奇数就分解成3和一个偶数。偶数再分解成两个素数。

上界只要3n就够了嘛。

剩下就是只剩输出的问题了。











#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <map>
#include <set> 
using namespace std;
const int n=100010;
bool a[100010];
int p[10000];

int num=0;
void Prime() {
    memset(a, 0, sizeof(a));
    int i, j;
    for(i = 2; i < n; ++i) {
        if(!(a[i])) p[num++] = i;
        for(j = 0; (j<num && i*p[j]<n); ++j) {
            a[i*p[j]] = 1;
            if(!(i%p[j])) break;
        }
    }
}
int pp[100010];
void PrimePair()
{
    for (int i=0;i<num;i++)
        for (int j=i;j<num;j++)
        {
            if (p[i]+p[j]>100000||(p[i]+p[j])%2==1)
                break;
            pp[p[i]+p[j]]=p[i];
        }
}
int para[100010],pos[100010];
int T;
int ss=0;
pair<int,int> out[500010];
void step(int s,int i,int j)
{ 
    if (!a[s]){
        out[ss++]=pair<int,int>(i,j);
        swap(pos[para[i]],pos[para[j]]);
        swap(para[i],para[j]);
        return ;
    }
    else{
        if (s%2==0)
        {
            step(2,j-1,j);
            step(j-i,i,j-1);
        }
        else
        {
            step(pp[s+1],j-pp[s+1]+1,j);
            step(j-i-pp[s+1]+2,i,j-pp[s+1]+1);
        }
    }
}

int main()
{
	//freopen("input","r",stdin);
	//freopen("output","w",stdout);
	Prime();
    memset(pp,0,sizeof(pp));
    PrimePair();
    pp[2]=2;
    cin>>T;
    ss=0;
    for (int i=1;i<=T;i++)
    {
        cin>>para[i];
        //para[i]=T+1-i;
        pos[para[i]]=i;
    }
    for (int i=1;i<=T;i++)
    {
        if (para[i]!=i)
            step(pos[i]-i+1,i,pos[i]);
    }
    cout<<ss<<endl;
    for (int i=0;i<ss;i++)
        cout<<out[i].first<<" "<<out[i].second<<endl;
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值