快考 NOIP 了。
T1 构造题
题目描述
给定一个正整数 \(n\),请构造一个长度为 \(n\) 的排列 \(p\),满足 \(\forall i \in [1, n - 1]\),\(p_i \oplus p_{i + 1}\) 不是质数,或报告无解。
数据范围:\(1 \leq \sum n \leq 5 \times 10^6\),\(1 \leq T \leq 10^5\)。
题解
如果两个数模 \(4\) 同余,则其二进制最后两位是一样的,因此这两个数异或结果是 \(4\) 的倍数!
因此,我们可以把 \(1 \sim n\) 分成 \(4\) 类,只需考虑如何合并这 \(4\) 类。
先说结论:假设 \(P_r(n)\) 表示 \([1, n]\) 中 \(\bmod 4 = r\) 的数从小到大排序得到的序列,则构造:
\[rev(P_1(n)) + rev(P_3(n)) + P_2(n) + P_0(n)
\]
合法。其中 \(rev\) 表示把整个序列翻转,即倒序。
简单证明一下:对于第一个 \(+\),显然 \(1\) 和大奇数异或是大偶数;对于第二个 \(+\),显然 \(3 \oplus 2 = 1\);对于第三个 \(+\),两个偶数异或一定得到偶数,而且当 \(n\) 足够大的时候,异或结果一定大于 \(2\),所以这样构造在 \(n\) 比较大的时候是正确的。
简单尝试一下,可以发现 \(n \geq 10\) 的时候可以直接这样构造。对于 \(n < 10\) 的情况,直接跑 dfs 暴搜即可。
参考代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){int x = 0, f = 1;char ch = getchar();while(!isdigit(ch)){if(ch == '-') f = -1;ch = getchar();}while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}return x * f;
}
inline void write(int x){if(x < 0) putchar('-'), x = -x;if(x > 9) write(x / 10);putchar(x % 10 + '0');return;
}
signed main(){freopen("constructive.in", "r", stdin);freopen("constructive.out", "w", stdout);int T = read();while(T--){int n = read();if(n < 10){if(n == 1) puts("1");if(n == 2) puts("-1");if(n == 3) puts("-1");if(n == 4) puts("-1");if(n == 5) puts("1 5 3 2 4");if(n == 6) puts("-1");if(n == 7) puts("1 5 3 7 6 2 4");if(n == 8) puts("1 5 3 2 4 8 6 7");if(n == 9) puts("4 2 6 7 3 5 1 8 9");continue;}for(int i = n; i >= 1; i--) if(i % 4 == 1) write(i), putchar(' ');for(int i = n; i >= 1; i--) if(i % 4 == 3) write(i), putchar(' ');for(int i = 1; i <= n; i++) if(i % 4 == 2) write(i), putchar(' ');for(int i = 1; i <= n; i++) if(i % 4 == 0) write(i), putchar(' ');putchar('\n');}return 0;
}