【笔记】一些构造题
构造题
1.题目(K)
简略题意:构造一个长度为n的排列,要求 \(\left | p_{i}-p_{ i \bmod n +1} \right |\) 为质数。不保证有解,\(2\le n\le 1e5\)。
思路
首先,我们已知题目要求使排列中所有相邻差的绝对值为质数。
因为质数中最常见且容易构造的是 \(2\) 这个数,所以我们希望尽量让相邻两数差等于 \(2\)。于是我们可以将:
- 所有奇数从小到大排列:\(1,3,5,7,...\)
- 所有偶数从大到小排列:\(...,6,4\)
这样无论是奇数内部还是偶数内部,相邻差都是 \(2\)。
但问题来了,对于奇数和偶数这两段之间的衔接:最大奇数(如 \(n\))和最大偶数(如 \(n-1\))之间的差是 \(1\),不是质数。而且首尾两数差也可能出现问题。那咋办?
这个时候我们注意到,\(2\) 和 \(n-1\) 比较特殊!?因为:
-
对于 \(2\),我们可以把它放在相邻两个奇数之间,这样差为质数。
例如:把 \(2\) 插在 \(5\) 和 \(7\) 之间,差分别是 \(3\) 和 \(5\),为质数 -
对于 \(n-1\),因为它挨着最大数 \(n\),它和 \(n\) 差为 \(1\),不符合条件。所以我们需要把 \(n-1\) 这个数挪出来,插到某个数旁边,形成质数差。也就是说:
- 当 \(n\) 为奇数时,即 \(n-1\) 为偶数,就把它插到奇数升序段中(在 \(n-6\) 后),来形成质数差;
- 当 \(n\) 为偶数时,即 \(n-1\) 为奇数,就把它插到偶数降序段中(在 \(n-4\) 后),来形成质数差。
例如: \(n=12\) 时,\(n-1=11\),把它插在 \(8\) 和 \(6\) 之间,差分别是 \(3\) 和 \(5\),都为质数。同时末尾剩的 \(4\) 和开头的 \(1\) 差 \(3\),也为质数(Nice!)
构造流程~:
- 第一步:当 \(n\le 4\) 时无解,输出 \(-1\);当 \(5\le n\le 8\) 时,我们直接打表,因为数字太少,没有足够的空间同时容纳这些特殊数以及亿些调整。额,对了,\(n=11\) 时直接打表更简单一些,因为插入两个特殊数后位置比较紧(输出到 \(5\) 时,会产生既想插入 \(2\),又想插入 \(n-1\),而产生冲突,导致两数差不为质数)。
(紧?!) - 第二步:对于上一步中特殊的 \(n\) 以外的 \(n\),先输出奇数(要升序,但跳过 \(2\) 和 \(n-1\) 这两个特殊数)。
- 第三步:在输出到 \(5\) 的时候,插入 \(2\)。
- 第四步:继续输出剩下的奇数,直到最大的奇数(当 \(n\) 为奇数时,并且输出到 \(n-6\) 时,插入 \(n-1\))。然后输出 \(n\)(最大数)作为奇偶段的转折。
- 最后一步:再输出偶数(要降序;当 \(n\) 为偶数时,并且输出到 \(n-4\) 时,插入 \(n-1\))。
为什么要在 $n-6$ 或 $n-4$ 时将 $n-1$ 插入?
-
当 \(n\) 为奇数,即 \(n-1\) 为偶数。奇数升序序列中相邻奇数差为 \(2\)。我们要把偶数 \(n-1\) 插入到两个奇数之间。在 \(n-6\) 后面插入 \(n-1\),即在 \(n-6\) 和 \(n-4\) 之间插入它,则:
- \(\left | \left ( n-1 \right ) -\left ( n-6 \right ) \right | =5\),是质数;
- \(\left | \left ( n-4 \right ) -\left ( n-1 \right ) \right | =3\),是质数。
所以在 \(n-6\) 后插入,这样简单~。
-
当 \(n\) 为偶数时,同上。
这样就构造完啦!时间复杂度 \(O(n)\)。
总结:本构造巧妙利用了奇偶差 \(2\) 打底,用 \(2\) 和 \(n-1\) 处理了交界的问题,因此实现了线性复杂度的构造~
代码
/*@author:decentz*/
#include<bits/stdc++.h>
#define FRE(x,y) freopen(#x".in","r",stdin);freopen(#y".out","w",stdout);
#define LK_TIME cerr<<(1.0*clock()/CLOCKS_PER_SEC)<<'\n';
#define REP(i,a,b) for(int i=(a),i##end=(b);i<=i##end;i++)
#define PRE(i,a,b) for(int i=(a),i##end=(b);i>=i##end;i--)
#define MMS(arr,n) memset(arr,n,sizeof(arr));
#define DEBUG(x) cerr<<#x<<": "<<x<<'\n';
using namespace std;
using LL=long long;
const LL INF=LLONG_MAX,_INF=LLONG_MIN;
bool STWZZ;
int n;
bool EDWZZ;
int main(){
//FRE(,);
//cerr<<(&EDWZZ-&STWZZ)/1024.0/1024.0<<'\n';
ios::sync_with_stdio(0);cin.tie(0);
cin>>n;
if(n<=4) cout<<-1;
else if(n==5) cout<<"4 1 3 5 2";
else if(n==6) cout<<"1 3 5 2 4 6";
else if(n==7) cout<<"1 3 5 2 7 4 6";
else if(n==8) cout<<"1 6 3 8 5 2 7 4";
else if(n==11) cout<<"1 3 10 5 2 7 9 11 8 6 4";
else{
for(int i=1;i<=n-2;i+=2){
cout<<i<<' ';
if(i==5) cout<<"2 ";
if(i==n-6) cout<<n-1<<' ';//针对奇数情况
}
cout<<n<<' ';
for(int i=n-(n&1)-2;i>=4;i-=2){//(n&1)用来判断n的奇偶
cout<<i<<' ';
if(i==n-4) cout<<n-1<<' ';//针对偶数情况
}
}
return 0;
}
代码中的 $ n-(n \\\& 1)-2 $ 是啥意思?
- 当 \(n\) 为偶数时($ (n\&1)=0 $,起始值 \(=n-2\)),最大的偶数是 \(n\),但 \(n\) 已经在前面的奇数升序序列结束后单独输出了,所以这里从 \(n-2\) 开始。
- 当 \(n\) 为奇数时($ (n\&1)=1 $,起始值 \(=n-3\)),最大的偶数是 \(n-1\),但 \(n-1\) 已经在前面的奇数序列中被插入过了(在 \(i==n-6\) 处),所以这里从 \(n-3\) 开始。
2.加急补充中……
因本人蒟蒻,所以如有错误请指出~

浙公网安备 33010602011771号