Codeforces 582A GCD Table
题意:有个长度为n(1 <= n <= 500)的序列(ai <= 1e9),然后这个序列两两元素之间的GCD可以构成一个表,比如对于序列{4, 3, 6, 2}就能构成下面的GCD表
问题就是,给你一个GCD表中的所有数字(无序的),然后根据这些数字把原序列反推出来
4
2 1 2 3 4 3 2 6 1 1 2 2 1 2 3 2
4 3 6 2
-----------
1
42
42
-----------
2
1 1 1 1
1 1
原题链接:http://codeforces.com/problemset/problem/582/A
我们知道gcd(a, b)肯定小于等于a跟b
那么对于输入的那么多数字,设其中最大的数为m,那么m肯定是原序列的数之一,因为如果m不在原序列当中,由于其他数都比m小,就导致不可能有其他两个数a, b使得gcd(a, b) = m,m就用不掉了,所以我们就先把m加入原序列了,m与m的最大公约数就是它自己,所以在所有输入的数当中,m就这样被用掉了(有多个m的话就是用掉了一个,大家懂我意思就行)。然后在剩下的数当中,我们又可以找出其中最大的数来,理由同上,这个数又加入原序列,这时我们反推得到的原序列已经确定两位了,新加入的数要被用掉当然不用说,然后这新加入的数跟之前的m来个gcd,这gcd出来的数是拿来填表的,根据对称性还填了两个,也就是说,用了两个gcd的数,以后找没用过的数时就不考虑它们了。再后面我们把没用过的数当中最大的拿出来,跟已经在原序列中的所有数gcd,重复上述过程,最后我们就能得到原序列的所有数。
简洁点说,就是设输入的所有数为table[],已知原序列a为空,每次从table[]中找到最大的数x,从table[]中删掉,之后把x跟a里面所有数的gcd从table[]中各删去2个,再把x这个数push_back到a的后面,如此往复,直到table为空,我们就得到了合法的原序列a
怎么知道哪些数没用过呢?我们可以考虑用个桶装数,但是1e9有点大,所以就得进行离散化处理。
怎么找最大的数?排个序就行了
AC代码:
#include<iostream> #include<algorithm> #include<vector> using namespace std; const int Maxn = 500 * 500 + 10; vector<int> numbers; // 离散化 vector<int> origin; //输入的数据 int Count[Maxn]; //桶(离散化处理过的) vector<int> a; int gcd(int n, int m) { return n % m == 0 ? m : gcd(m, n % m); } int get(int x) { return lower_bound(numbers.begin(), numbers.end(), x) - numbers.begin(); } int main() { ios::sync_with_stdio(false); int n; cin >> n; for (int i = 0; i < n * n; i++) { int x; cin >> x; origin.push_back(x); numbers.push_back(x); } sort(origin.begin(), origin.end()); //排序 //离散化处理 sort(numbers.begin(), numbers.end()); numbers.erase(unique(numbers.begin(), numbers.end()), numbers.end()); //装桶 for (int i = 0; i < origin.size(); i++) Count[get(origin[i])]++; //问题解决部分 for (int i = origin.size() - 1; i >= 0; i--) { if (Count[get(origin[i])] == 0) continue; int x = origin[i]; a.push_back(x); Count[get(x)]--; for (int j = 0; j < a.size() - 1; j++) Count[get(gcd(a[j], x))] -= 2; } for (int i = 0; i < a.size(); i++) cout << a[i] << " "; return 0; }