【Codeforces Round #694 (Div. 1) B】Strange Definition
题目链接
翻译
每秒钟会对原数组进行如下操作,对于每一个数组中的元素。找到整个数组中和它【相关】的数字,将这些数字全都乘起来
然后用来代替这个元素。
【相关】表示两个数字的最小公倍数和最大公因数的商是一个完全平方数。
然后让你回答 \(q\) 个询问,表示 \(w\) 秒钟之后,数组中每个元素【相关】的数字的个数的最大值。
题解
得知道 \(lcm(x,y)=\frac{x*y}{gcd(x,y)}\) 这样的话,就知道两个数字相关当且仅当他们相乘是一个完全平方数(分母已经是 \(gcd\) 的平方了)。
那么什么样的两个数字 \(x\) 和 \(y\) 会满足相乘是一个完全平方数呢?
从质因数分解的角度考虑。
考虑两个数字分解之后所有的 公共 质因子 \(p\),如果两者分解出来的指数 \(q1\) 和 \(q2\) 的和都是偶数。
那么这是它们的乘积是完全平方数的一个先决条件。
还有非公共的部分,这部分咋办? 会发现因为不是公共的质因子,所以如果有一方对应的非公共质因子的指数为奇数。
那么它们相乘就不是完全平方数了,比如 \(2=2\) 和 \(6=2*3\) 其中 \(6\) 的质因子 \(3\) 不是公共质因子,且指数为奇数 \(1\)。
所以它们相乘就不是完全平方数了,因此非公共质因子的指数都要为偶数。
而公共质因子的指数相加为偶数,先决条件就是它们俩的指数的奇偶性相同。
也即对应的质因子指数 \(q_i\%2\) 的值要相同。
则我们可以这样,对于数组中所有数字的质因数分解,把它都写成 \(x=p_1^{q_1\%2}*p_2^{q_2\%2}...*p_k^{q_k\%2}\) 的形式。
这样我们就可以根据这个新的 \(p_1^{q_1\%2}*p_2^{q_2\%2}...*p_k^{q_k\%2}\) 标识(运算的结果),相同标识的分到一个集合中去,将原数组中的数字分成很多个不同的集合了。
会发现 同一个集合里面的数字都是互相相关的,相乘之后指数都是偶数。且非公共质因子的指数也用这个模 \(2\) 操作区分开来了,如果为偶数
就直接变成数字 \(1\) 了,不会成为集合的标识。
那么我们就得到很多个集合了,有什么用呢?
一开始 \(0\) 时刻,我们只要统计所有的集合的大小的最大值即可。
大于 \(0\) 时刻,现在要开始变化了。
会发现,如果集合的大小为偶数,那么里面所有的数字相乘的话,最后的结果就是所有新的数字的指数都变成偶数,那么在指数模 \(2\) 的情况下,就全都变成数字 \(1\) 了。
则对应集合会和以 \(1\) 为标识的集合合并到一起。
而集合的大小为奇数的话,那么每个数字的质因子的指数在经过奇数次相加之后,还是一个奇数, 则集合大小不变,里面的数字也不变(指数还是都是 \(1\))。
所以,统计一下 \(1\) 集合,以及集合大小为偶数的集合,记作 \(cnt1\),然后一开始所有集合中大小最大的那个集合的大小,记作 \(cnt2\),这里的 \(cnt2\) 能够覆盖
奇数大小集合不变的情况。
所以在 \(w>=1\) 的时候,输出 \(max(cnt1,cnt2)\) 而在 \(w=0\) 的时候,直接输出 \(cnt2\) 就好,代码中用的不是 \(cnt\) 等名称, 不要 \(confuse\) 了...
代码
#include <bits/stdc++.h>
#define LL long long
using namespace std;
int n;
map<int,int> dic;
int main() {
#ifdef LOCAL_DEFINE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(0), cin.tie(0);
int T;
cin >> T;
while (T--) {
dic.clear();
cin >> n;
for (int i = 1;i <= n; i++){
int x;
cin >> x;
for (int j = 2;j*j <= x; j++){
while (x%(j*j)==0){
x/=j*j;
}
}
dic[x]++;
}
int a = 0, b = 0;
for (auto tmp: dic){
if (tmp.first==1 || tmp.second%2==0){
b+=tmp.second;
}
if (tmp.second > a){
a = tmp.second;
}
}
int q;
cin >> q;
for (int i = 1;i <= q; i++){
LL w;
cin >> w;
if (w == 0){
cout << a << endl;
}else{
cout << max(a,b) << endl;
}
}
}
return 0;
}