AGC003D Anticube

题目链接

对于一个数的唯一分解:(x=p_1^{k_1} imes p_2^{k_2} imes cdots imes p_n^{k_n}),显然我们把每个 (k_i) 都对 (3) 取模对答案没有任何影响(除了 (x) 本身就是完全立方数的特殊情况)。

考虑取模后每一个 (x) 都有唯一确定的 (y) 使得 (x imes y) 是一个完全立方数,我们显然可以贪心的选择 (x,y) 中数量较多的并 ban 掉另一个。

现在问题的关键是如何取模呢?直接分解质因数时间复杂度显然不能容忍。

考虑先把 (10^{frac {10} 3}) 以内的质因子全部筛掉,现在一个数 (x) 只有三种情况:

  1. 最后剩的数是 (1),是特殊情况,拉到一起特判处理。
  2. 剩的数不是完全立方数,那么对立面就一定是这个数的平方。
  3. 是完全平方数,对立面是这个数的二次方根。

可以用 (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
", ans + qwq);
}

signed main()
{
	prework();
	init();
	work();
	return 0;
}

原文地址:https://www.cnblogs.com/With-penguin/p/13818363.html