题解:CF2039D Shohag Loves GCD

从给定集合中选数并构造序列 {ai}\{a_i\}{ai},需要满足 agcd⁡(i,j)≠gcd⁡(ai,aj)∣∀1≤i<j≤na_{\gcd (i,j)} \neq \gcd(a_i,a_j) \mid \forall 1 \le i < j \le nagcd(i,j)=gcd(ai,aj)∀1i<jn 并且构造出的序列字典序最大。

不妨从反面分析,考虑满足 agcd⁡(i,j)=gcd⁡(ai,aj)a_{\gcd (i,j)} = \gcd (a_i,a_j)agcd(i,j)=gcd(ai,aj)。当 i∣ji \mid jij 时,gcd⁡(i,j)=i\gcd (i,j) = igcd(i,j)=iai∣aja_i \mid a_jaiaj。进一步的,当 i∤ji \nmid jij 时,是否也存在一个 iii,满足 agcd⁡(i,j)=gcd⁡(ai,aj)a_{\gcd (i,j)} = \gcd (a_i,a_j)agcd(i,j)=gcd(ai,aj) 呢?

假设存在这个 iii,我们设 g=gcd⁡(i,j)g = \gcd (i,j)g=gcd(i,j),那么 ag=gcd⁡(ai,aj)a_g = \gcd (a_i,a_j)ag=gcd(ai,aj)g<ig < ig<i。由于 g∣ig \mid igi,等价于 g=gcd⁡(g,i)g = \gcd (g,i)g=gcd(g,i),由上面的结论可知 agcd⁡(g,i)=gcd⁡(ag,ai)a_{\gcd (g,i)} = \gcd (a_g,a_i)agcd(g,i)=gcd(ag,ai)。此时与题目的条件不符合,故假设不成立。

所以说在构造 aia_iai 时,只需要考虑序列以 iii 的因数(且小于 iii)为下标的数,预处理因数即可。在构造的时候,我们可以将所有以 iii 的因数为下标的数放入 set\mathrm{set}set 中,然后从最大的数开始,查找到第一个未出现在集合中的数即可退出循环。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <set>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 1e5 + 5;
const int M = 1e5; 
const int MOD = 1e9 + 7;
inline int read ();
vector <int> ans,p[MAX];
int t,n,m,a[MAX];
int main ()
{
	//freopen (".in","r",stdin);
	//freopen (".out","w",stdout);
	for (int i = 1;i <= M;++i)
		for (int j = i + i;j <= M;j += i) p[j].push_back (i);//j 的因数 
	t = read ();
	while (t--)
	{
		n = read ();m = read ();
		for (int i = 1;i <= m;++i) a[i] = read ();
		sort (a + 1,a + 1 + m);ans.clear ();
		for (int i = 1;i <= n;++i)
		{
			set <int> invaild;
			for (auto item : p[i]) invaild.insert (ans[item - 1]);
			for (int j = m;j;--j)
			{
				if (invaild.find (a[j]) == invaild.end ())
				{
					ans.push_back (a[j]);
					break;
				}
			}
			if (ans.size () != i) break;
		} 
		if (ans.size () != n) puts ("-1");
		else {for (auto item : ans) printf ("%d ",item);puts ("");} 
	}
	return 0;
}
inline int read ()
{
    int s = 0;int f = 1;
    char ch = getchar ();
    while ((ch < '0' || ch > '9') && ch != EOF)
	{
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9')
	{
        s = s * 10 + ch - '0';
        ch = getchar ();
    }
    return s * f;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值