[数论记录]AT2004 [AGC003D] Anticube

command_block

2021-01-12 10:33:52

Personal

**题意** : 给定一个长度为 $n$ 的整数集合 $S$ ,选取一个最大的 $T\subseteq S$ ,使得 $T$ 中没有两个数的乘积是完全立方数。 $n\leq 10^5,S\subseteq[1,10^{10}]$ ,时限$\texttt{5s}$。 ------------ 一个显然的做法是,将每个数质因数分解,然后次数模 $3$ 归类。 这样,一个类和恰好另一个类对应乘积是完全立方数。 特殊地,完全立方数本身对应自己,所以至多选一个完全立方数。 否则,在一对配对的类中,选出大小较大的。 但是,在这个数据范围下没有使用的质因数分解方法,考虑分析性质。 将所有完全立方数特殊处理。 先把 $\leq s^{1/3}$ 的质数都暴力除掉,这部分复杂度为 $O(ns^{1/3}/\log s)$。 此时,余下的部分可能形如 $p,p^2,p_1p_2$ 。 对于 $p^2$ 的情况,需要补乘 $p^{3k+1}$ 才能变成立方,显然只有可能是 $p$。 对于其他情况,如余下 $m$,需要补乘 $m^{3k+2}$ 才能变成立方,显然还只有可能是 $m^2$。 注意 $m^2$ 可能超出 $s$,甚至爆 `long long`。 于是,我们按照 ($s^{1/3}$以内质因子模 $3$,剩余质因子配对) 来分类即可。 ```cpp #include<algorithm> #include<cstdio> #include<cmath> #include<map> #define ll long long #define mp make_pair #define Pr pair<ll,ll> #define MaxS 2250 using namespace std; const int lim=2200; const ll INF=10000000000ll; int p[MaxS/4],tn; bool e[MaxS]; void sieve() { for (int i=2;i<=lim;i++){ if (!e[i])p[++tn]=i; for (int j=1;j<=tn&&i*p[j]<=lim;j++){ e[i*p[j]]=1; if (i%p[j]==0)break; } } } int n,ans=1,flag; map<Pr,int> sav; void calc(ll x) { ll t=pow(x,1.0/3); t=max(1ll,t-3);while(t*t*t<x)t++; if (t*t*t==x){flag=1;return ;} ll t1=1; for (int i=1;i<=tn;i++){ int c=0; while(x%p[i]==0){c++;x/=p[i];} c%=3; for (int t=1;t<=c;t++)t1*=p[i]; }sav[mp(t1,x)]++; } Pr mir(Pr a) { ll t1=1,x=a.first,y=a.second,t=sqrtl(y); if (t*t!=y){ if (y>1000000000)return mp(0,0); t=y*y; } for (int i=1;i<=tn;i++){ int c=0; while(x%p[i]==0){c++;x/=p[i];} c=(6-c)%3; for (int t=1;t<=c;t++){ t1*=p[i]; if (t1>=INF)return mp(0,0); } }return mp(t1,t); } int main() { scanf("%d",&n); sieve(); for (int i=1;i<=n;i++){ ll x; scanf("%lld",&x); calc(x); } map<Pr,int>::iterator it=sav.begin(); for (;it!=sav.end();it++){ Pr buf=mir(it->first); if (!sav.count(buf)) ans+=it->second*2; else ans+=max(it->second,sav[buf]); }printf("%d",ans/2+flag); return 0; } ```