Agc003_D AntiCube

传送门

题目大意

给定$N$个数,求一个最大的子集,使得任意两两的乘积不是一个完全立方数。

$nleq 10^5 A_ileq 10^{10}$

题解

考虑两两乘积为$x^3$,由于$x^3leq 10^{20}$,那么$xleq 10^{frac{20}{3}}$,那么$x$最多出现一个超过$10^{frac{10}{3}}$的质因子,且这一质因子不会超过$10^{frac{20}{3}}$。又由于$A_ileq 10^{10}$,所以至多出现一个$> 10^5$的质因子,由于$x^3$由两个$A_i$相乘,那么$x$一定不会出现超过$>10^5$的质因子。

所以$x$由$[2,10^5]$内的质数幂构成,且$[10^{frac{10}{3}},10^5]$内质数至多出现$1$个,且指数不超过$1$。

那么考虑筛出$10^5$以内的质数。

对于每一个$A_i$我们直接暴力枚举$<10^{frac{10}{3}}$的质数(大约$300$个左右),直接筛掉,过滤掉质数是$3$的倍数的质因子,记录它由哪几个剩下的质因子构成,并将它每一个质因子质数模$3$的余数($1$或$2$)压进一个二进制状态中。对于剩下的数,如果它不为$1$,我们判断它是不是一个$<10^5$的质数或者一个$<10^5$的质数的平方,如果不是,那么它一定无法和别的任何一个数组成立方数,因为它肯定是一个$>10^5$的质数。否则,我们直接把这个稍大的质数当做之前过滤的数一样压入状态中。

对于两个过滤完的质因子的集合完全相同的数,它们能组成立方数当且仅当它们的二进制状态是恰好相反的,这样就能凑出每一个质因子的质数都是$3$的倍数。

然后就可以直接按照质因子集合放在一起,记录每一种状态和它的补集包含的数的数量,选集合大小更大的那一堆即可。

复杂度$O(nlog n+300n)$。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 300020
#define MAXN 100000
using namespace std;
int n,m,sz[M],K[M],res,mp[200002];
int od[M],ans; LL p[M],pri[M],t[M]; bool isp[M];
bool cmp(int x,int y){return t[x]<t[y];}
LL read(){
	LL nm=0; char cw=getchar(); for(;!isdigit(cw);cw=getchar());
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0'); return nm;
}
int main(){
	memset(isp,true,sizeof(isp));
	for(int i=2;i<MAXN;i++){
		if(!isp[i]) continue; pri[++m]=i;
		for(int j=(i<<1);j<MAXN;j+=i) isp[j]=false;
	}
	n=read();
	for(int j,i=1;i<=n;i++){
		for(p[i]=read(),K[i]=sz[i]=0,j=t[i]=1;j<=308;j++){
			if(p[i]%pri[j]) continue; int cnt=0;
			while(!(p[i]%pri[j])) p[i]/=pri[j],++cnt; cnt%=3;
			if(cnt) K[i]|=((cnt&1)<<sz[i]),sz[i]++,t[i]*=pri[j];
		}
		if(p[i]>1ll){
			if(p[i]<MAXN) K[i]|=(1<<sz[i]),sz[i]++,t[i]*=p[i];
			else {
				LL qq=sqrt(p[i]);
				if(qq*qq!=p[i]) ans++,i--,n--;
				else sz[i]++,t[i]*=qq;
			}
		}
	}
	for(int i=1;i<=n;i++) od[i]=i; sort(od+1,od+n+1,cmp);
	for(int l=1,r;l<=n;l=r+1){
		for(r=l;r<n&&t[od[r]]==t[od[r+1]];r++);
		if(t[od[l]]==1){ans++;continue;}
		int maxn=(1<<sz[od[l]])-1;
		for(int i=l;i<=r;i++) mp[K[od[i]]]++;
		for(int i=l;i<=r;i++){
			int x=K[od[i]],ot=(maxn^K[od[i]]);
			if(mp[x]>=mp[ot]) ans+=mp[x],mp[x]=mp[ot]=0;
		}
	} printf("%d
",ans); return 0;
}
原文地址:https://www.cnblogs.com/OYJason/p/9837304.html