Codeforces Round #694 (Div. 1) B. Strange Definition
题意描述:给你\(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性质、质因子的角度去思考,而不是想通过乱搞(少数)去解决(数学还是太折磨了)