AGC003D Anticube
对于一个数的唯一分解:\(x=p_1^{k_1}\times p_2^{k_2}\times \cdots \times p_n^{k_n}\),显然我们把每个 \(k_i\) 都对 \(3\) 取模对答案没有任何影响(除了 \(x\) 本身就是完全立方数的特殊情况)。
考虑取模后每一个 \(x\) 都有唯一确定的 \(y\) 使得 \(x\times y\) 是一个完全立方数,我们显然可以贪心的选择 \(x,y\) 中数量较多的并 ban 掉另一个。
现在问题的关键是如何取模呢?直接分解质因数时间复杂度显然不能容忍。
考虑先把 \(10^{\frac {10} 3}\) 以内的质因子全部筛掉,现在一个数 \(x\) 只有三种情况:
- 最后剩的数是 \(1\),是特殊情况,拉到一起特判处理。
- 剩的数不是完全立方数,那么对立面就一定是这个数的平方。
- 是完全平方数,对立面是这个数的二次方根。
可以用 \(map\) 记录每个数出现的次数,最后注意所有完全立方数只能取一个。时间复杂度 \(O(\sqrt[3]{V}nlogn)\)。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#include<cmath>
#define int long long
using namespace std;
const int N = 100009, K = 2200;
int n, a[K + 9], p[K + 9], b[N], cnt;
map <int, int> Map, Rev;
void prework()
{
for (int i = 2; i <= K; i++)
{
if (!a[i])
a[i] = i, p[++cnt] = i;
for (int j = 1; j <= cnt; j++)
{
if (p[j] > a[i] || p[j] > K / i)
break;
a[p[j] * i] = p[j];
}
}
}
void init()
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &b[i]);
}
void work()
{
int qwq = 0;
for (int i = 1; i <= n; i++)
{
int x = 1, rev = 1;
for (int j = 1; j <= cnt; j++)
if (b[i] % p[j] == 0)
{
int tmp = 0;
while (b[i] % p[j] == 0)
tmp++, b[i] /= p[j];
tmp %= 3;
if (tmp == 1) x *= p[j], rev *= p[j] * p[j];
else if (tmp == 2)
x *= p[j] * p[j], rev *= p[j];
}
x *= b[i];
int Sqr = (int)sqrtl(b[i]);
if (Sqr * Sqr == b[i])
rev *= Sqr;
else if (rev <= 1e18 / b[i] / b[i])
rev *= b[i] * b[i];
else rev = -1;
if (x == 1) qwq = 1;
else Rev[x] = rev, Map[x]++;
}
int ans = 0;
map <int, int> ::iterator it;
for (it = Map.begin(); it != Map.end(); it++)
{
int FI = it -> first, SE = it -> second;
if (Rev[FI] == -1)
{
ans += SE;
continue;
}
if (Rev[FI] == -2) continue;
ans += max(SE, Map[Rev[FI]]);
Rev[Rev[FI]] = -2;
}
printf("%lld\n", ans + qwq);
}
signed main()
{
prework();
init();
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!