CF582A 题解
有意思的思维题。
题意简述
由一个数列 $\{a_1,a_2,\cdots,a_{n-1},a_n\}(1\le n\le 500)$ 构造数表 $b$,其中 $b_{i,j}=\gcd(a_i,a_j)$。现在给出打乱之后的 $b$ 的所有元素,要求还原 $a$。
题目分析
这个题的突破口在于这样一个结论:表中最大的数 $m$,一定在原序列中出现。
证明:设 $m=\gcd(a_i,a_j)$(不妨设 $a_i\le a_j$),则 $m\le a_i\le a_j=\gcd(a_j,a_j)$。而由于 $\gcd(a_j,a_j)$ 必为矩阵里的数,且 $m$ 是表中最大的数,因此 $m\ge \gcd(a_j,a_j)$。所以 $m=\gcd(a_j,a_j)=a_j$,必在原序列中出现。
找到最大值之后就十分简单了。我们找到了最大值 $m$,也就求出了一项 $a_j$。我们只需要从表中剔除目前已知的与 $a_j$ 有关的所有 $\gcd(a_i,a_j)$ 后重复执行该过程即可。
代码实现
STL 浓度过高不喜勿喷
#include<bits/stdc++.h>
using namespace std;
int n,x;
map<int,int>S;
vector<int>ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n*n;i++)
scanf("%d",&x),S[x]++;//把每个数直接扔进桶里
while(!S.empty())//桶里还有就继续
{
auto i=S.end();
i--;//找到最大的数对应的迭代器
for(auto j:ans)//遍历 a 中已经确定的数
{
int tmp=__gcd(i->first,j);
S[tmp]-=2;//删掉和当前最大数有关的所有 gcd(a_i,a_j),注意每个数在表里都出现 2 次,所以个数-=2。
if(!S[tmp])//删没了就把迭代器也给删了
S.erase(tmp);
}
ans.push_back(i->first);//把最大数扔到答案里
i->second--;//删掉 gcd(a_j,a_j)
if(!i->second)
S.erase(i);//删没了就把迭代器也给删了
}
for(auto i:ans)
printf("%d ",i);//输出
return 0;
}