洛谷P9573 「TAOI-2」核心共振题解

题目传送门

题意:

构造一个 \(1\sim n\) 的排列,使得其中两个相邻的数之和为 \(p\) 的倍数,且这种情况在序列中尽量多的存在。

分析:

我采用了由特殊到一般的方法。以 \(p = 2\) 为例,要想使两个数之和为 \(2\) 的倍数,那么肯定是两个偶数或两个奇数进行两两组合。

想到这里不妨放开思路:奇数和偶数在这里究竟是什么样的存在呢?很容易发现,奇数除以 \(2\) 余数是 \(1\) ,偶数则为 \(0\) ,所以我们完全可以把 \(1\sim n\) 中的所有数分成两类:除以 \(2\) 余数为 \(0\) 的和除以 \(2\) 余数为 \(1\) 的。

这很像 hash 的处理思路,将 \(1\sim n\) 中的所有数都对 \(p\) 取模后得到在 \(0\sim p - 1\) 之间的数。也就是说:\(1\sim n\) 上的所有数都可以映射到 \(0\sim p - 1\) 上!

那么接下来就好办了。首先考虑特殊情况,所有的数都是 \(1\) 的倍数,所以当 \(p = 1\) 时,直接按 \(1\sim n\) 输出即可。同时如果 \(p\) 太大了,连最大的两个数 \(n - 1\)\(n\) 相加都没它大,那此时不可能产生共振,也是直接按 \(1\sim n\) 输出即可。

一般情况下,将模为 \(0\) 的数先输出出来,因为它们本就是 \(p\) 的倍数了,相加后还是 \(p\) 的倍数。然后设这两个数分别为 \(a\) , \(b\) ,要使 \(a + b\)\(p\) 的倍数,则需要满足 \(a \bmod p + b \bmod p = p\) ,即将余数为 \(i\) 和余数为 \(p - i\) 的两组数交替输出即可。

\(p\) 是奇数,那在除去 \(0\) 这一组后刚好形成两两搭配;若 \(p\) 是偶数,那还有一组是落单的,发现其实落单的那组中的成员数两两相加也是 \(p\) 的倍数,所以直接输出这一组就行了。

代码:

#include <iostream>
#include <cstring>

using namespace std;
const int N = 200010; 
int t, n, p;

inline int read() {	
    char ch = getchar();
	int x = 0, f = 1;
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    } 
	while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    } 
    return x * f;
}

int main() {
	t = read();
	while(t--) {
		n = read();
		p = read();
		if(p == 1 || p > 2 * n - 1) { //特判 
			for(int i = 1; i <= n; i++) printf("%d ", i);
			continue;
		}
		for(int i = p; i <= n; i += p) { //0
			printf("%d ", i);
		}
		for(int i = 1; i < p; i++) { //1 ~ p - 1
			if(i >= p - i) break; //若前面的组比后面的组编号还大说明输出完了 
			for(int j = 0; j <= n; j += p) {
				if(i + j <= n) printf("%d ", i + j); 
				if(p - i + j <= n) printf("%d ", p - i + j); //在n的范围内才输出 
			}
		}
		if(p % 2 == 0) { //p为偶数则输出中间那组 
			for(int i = p / 2; i <= n; i += p) {
				printf("%d ", i);
			}
		}
	}
	return 0; //完结撒花! 
}

posted @ 2023-09-26 15:41  Brilliant11001  阅读(25)  评论(0编辑  收藏  举报