【题解】 CF1419E Decryption 格雷码+构造

Legend

Link \(\textrm{to Codeforces}\)

Editorial

考虑分解质因数 \(n=\prod_{i=0}^{m-1} p_i^{k_i}\)

我们不难把某个数字是否分别存在这些质因数状压成二进制数。

去除了 \(1\) 之后我们的二进制数的值域是 \((0,2^m)\) 的开区间。

这容易让人想到格雷码,每次只切换一位,并且去除 \(0\) 之后相邻两项与一定不为 \(0\)

所以如果这题把环改成序列,就可以把所有数字按格雷码顺序来构造答案就行了。

不过是个环,咋办呢?

我的解决办法是把 \(U=2^{m}-1\) 到末尾的格雷码翻转,这样全集 \(U\) 被翻到最末尾,一定可以和头部有交。

最末尾的被翻到全集的位置,它和前面的交起来,最高位一定是 1。

除了 \(m=2\),这样构造都是正确的。我们总能构造出答案为 \(0\) 的方案。

现在讨论 \(m=2\) 的情况。

如果 \(n=pq\),其中 \(p,q\) 均为质数,那么手动暴力枚举可以知道,答案为 \(1\)

否则,质因数为 \(U\) 的数字至少有 2 个,我们只要构造 \(1,\cdots,1,3,2,\cdots,2,3\cdots,3\) 这样的序列就行了。

Code

#include <bits/stdc++.h>

#define debug(...) fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)\
	freopen(#x".in" ,"r" ,stdin);\
	freopen(#x".out" ,"w" ,stdout)
#define LL long long

const int MX = 1e5 + 23;
const LL MOD = 998244353;

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

std::vector<int> p ,fac[1 << 10] ,code;
int st[MX * 2];

void solve(){
	p.clear();
	code.clear();
	for(int i = 0 ; i < (1 << 10) ; ++i)
		fac[i].clear();

	std::vector<int> tmp;
	int n = read() ,cn;
	cn = n;
	for(int i = 2 ; i * i <= cn ; ++i){
		if(cn % i) continue;
		p.push_back(i);
		while(cn % i == 0) cn /= i;
	}
	if(cn != 1) p.push_back(cn);
	for(int i = 1 ; i * i <= n ; ++i){
		if(n % i == 0){
			if(i != 1) tmp.push_back(i);
			if(i * i != n) tmp.push_back(n / i);
		}
	}
	for(int j = 0 ; j < (int)tmp.size() ; ++j)	
		st[j] = 0;
	for(int i = 0 ; i < (int)p.size() ; ++i){
		for(int j = 0 ; j < (int)tmp.size() ; ++j){
			if(tmp[j] % p[i] == 0) st[j] |= 1 << i;
		}
	}
	for(int i = 0 ; i < (int)tmp.size() ; ++i)
		fac[st[i]].push_back(tmp[i]);

	if(p.size() == 2u){
		if(n == p[0] * p[1]){
			for(int i = 1 ; i <= 3 ; ++i)
				for(auto j : fac[i])
					printf("%d " ,j);
			puts("\n1");
		}
		else{
			for(auto j : fac[1]) printf("%d " ,j);
			printf("%d " ,*fac[3].rbegin());
			fac[3].pop_back();
			for(auto j : fac[2]) printf("%d " ,j);
			for(auto j : fac[3]) printf("%d " ,j);
			puts("\n0");
		}
	}
	else{
		int aim = 0;
		for(int i = 0 ; i < (int)(1 << p.size()) ; ++i){
			code.push_back(i ^ (i >> 1));
			if((i ^ (i >> 1)) == (1 << p.size()) - 1){
				aim = i;
			}
		}
		std::reverse(code.begin() + aim ,code.begin() + (1 << p.size()));
		for(int i = 0 ; i < (int)(1 << p.size()) ; ++i)
			for(int j : fac[code[i]])
				printf("%d " ,j);
		puts("\n0");
	}
	return ;
}

int main(){
	int T = read();
	while(T--) solve();
	return 0;
}
posted @ 2021-05-02 15:39  Imakf  阅读(92)  评论(0编辑  收藏  举报