题意描述:给你(n)个数,定义假设对于某个集合,里面任意两数(lcm(x,y)/gcd(x,y))为完全平方数即合法,把这n个数变成为很多这样的集合(每个数只能用1次)。
最开始给的n个数为第0秒,每一秒钟之后,每个集合内的每一个数会变为该集合所有数的乘积,现在有(q)次询问,问你在(w)秒时其中最大的集合的大小。
(1<=n<=3e5)
(1<=a<=1e6)
(1<=q<=3e5)
(0<=w<=1e18)
做法:(lcm(x,y)/gcd(x,y)=x*y/(gcd(x,y)*gcd(x,y))),即xy的乘积除以(gcd(x,y))的平方。那么满足这种情况的两数,它们相乘之后每个质因子的数量都得是偶数:
那么我们通过质因数分解每一个(x),把(x)的数量为奇数的质因子乘在一起,这样得到一个(res),当另一个数(y)要与(x)满足条件是,(y)的数量为奇数的质因子乘在一起也必然是(res)。
对于之前每一个(res)出现了多少次取一个max,得到的就是第(0)秒的答案(ans0)。
之后每个集合内的每一个数会变为该集合所有数的乘积,若集合的大小为奇数,则他们的乘积质因数分解之后还是原来res,若为偶数则(res)变为(1),可以合并到(res=1)的情况。这样第一秒时就只剩那些集合大小为奇数的(res!=1)的集合以及(res=1)的集合,易知之后不管过了多少秒,答案都不会改变。
细节处理:①有时候最后(res!=1)的集合大小反而大于(res=1)时集合的大小,需要两个之前取一个max。②一般是遍历每一个(res)去找偶数大小的集合,(res=1)的时候集合大小也可能为偶数,不要加重复了。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL)
double pi = acos(-1);
const double eps = 1e-7;
const int inf = 1e9 + 7;
const int maxn = 1e6 + 10;
ll mod = 1000000007;
map<int, int>mp;
int v[maxn], p[maxn], cnt;
void Euler_sieve(int n)
{
cnt = 0;
for (int i = 2; i <= n; i++) {
if (v[i] == 0) { v[i] = i; p[++cnt] = i; }
for (int j = 1; j <= cnt; j++)
{
if (p[j] > v[i] || p[j] > n / i)break;
v[i * p[j]] = p[j];
}
}
}
int main()
{
fastio;
int t;
cin >> t;
Euler_sieve(1e6 + 5);
while (t--)
{
mp.clear();
int n;
cin >> n;
int ans1 = 0, ans2 = 0;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
int res = 1;
while (x > 1)
{
int tot = 0;
int tmp = v[x];
while (x % tmp == 0)
{
x /= tmp;
tot++;
}
if (tot & 1)
res *= tmp;
}
mp[res]++;
ans1 = max(mp[res], ans1);
}
for (auto i : mp)
if (i.second % 2 == 0)
ans2 += i.second;
else if (i.first == 1)
ans2 += i.second;
ans2 = max(ans2, ans1);
int q;
cin >> q;
while (q--)
{
ll w;
cin >> w;
if (!w)
cout << ans1 << endl;
else cout << ans2 << endl;
}
}
return 0;
}
总结:这类数学题大多得去从gcd性质、质因子的角度去思考,而不是想通过乱搞(少数)去解决(数学还是太折磨了)