标签:个数 abi 因子 ext inline scan 相同 actor tin
给出n(<=300)个数(<=1e18),它们的乘积是x。对于任意\(d_i\mid x\),有\(d_i^{k_i} \mid x,d_i^{k_i+1}\nmid x\),求最大的\(k_i\),并输出当\(k_i\)最大时,有多少个\(d_i\)满足条件。
首先,这道题的思路肯定是对x分解质因数,也就是对n个数分解质因数,然后求出质因数的最多出现次数c,和出现次数最多的质因数个数d,答案分别是\(c\)和\(2^d\)。
现在问题来了,给出的一个数最多可以到\(10^{18}\),怎么分解质因数呢?先把小于1e6的质数都筛出来,把它们对这n个数试除。除完了以后,n个数中,没有数的因子小于1e6。因此,每个数x最多由两个质数相乘。
到这一步以后,我们就可以通过两两gcd把出现一次以上的质因子确定出来。剩下的数中的质因子,肯定只会在自己那个数中出现。因此,通过\(\sqrt{n}^2\)的方法和Miller-Rabin算法,就可以判断出这个数是由单独一个质因子组成,还是由两个相同质因子组成,抑或是由两个不同质因子组成。对于只出现过一次的质因子,我们并不关心它的值。
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
//////Miller-Rabin素数判定//////
const LL m=7, A[m]={2, 3, 5, 7, 11, 13, 17};
LL fmul(LL a, LL b, LL p){ //将b分解为二进制,返回a*b%p
LL ans=0;
for (; b; b>>=1, a+=a, a%=p)
if (b&1) ans+=a, ans%=p;
return ans;
}
LL fpow(LL a, LL x, LL p){
LL ans=1, base=a;
for (; x; x>>=1, base=fmul(base, base, p))
if (x&1) ans=fmul(ans, base, p);
return ans;
}
bool MR(LL a, LL x, LL p){ //判断是否a^x=1或p-1 (mod p),且mr下去也成立
LL t=fpow(a, x, p);
if (t!=1&&t!=p-1) return false;
if (t==1&&x&1||t==p-1) return true;
return MR(a, x>>1, p);
}
bool isprime(LL p){
if (p&1==0) return false;
for (LL i=0; i<m; ++i){
if (p==A[i]) return true; //互质时费马小定理才成立
if (fpow(A[i], p-1, p)!=1) return false;
if (!MR(A[i], (p-1)>>1, p)) return false;
}
return true;
}
const LL maxn=305, maxs=1e6+5;
LL n, a[maxn], tmp[maxn], tmpn, c[maxn];
LL p[maxs], cntp, factor[maxs];
void sieve(LL n){
for (LL i=2; i<n; ++i){
if (factor[i]) continue;
p[cntp++]=i;
for (LL j=i; j<n; j+=i) factor[j]=i;
}
}
LL allp[maxn*60], tl;
LL b[maxn*maxn], tl2; //extrap1:没有被列入allp的,出现次数为1的质数还有多少个
LL appear[maxn*60];
LL gcd(LL x, LL y){ return y?gcd(y, x%y):x; }
LL sqr(LL x){ return x*x; }
LL x[maxn*2];
int main(){
//freopen("number.in", "r", stdin); freopen("number.out", "w", stdout);
sieve(1e6);
scanf("%lld", &n);
for (LL i=0; i<n; ++i){
scanf("%lld", &a[i]);
for (LL j=0; j<cntp; ++j)
while (a[i]%p[j]==0){
a[i]/=p[j]; allp[tl++]=p[j]; }
}
for (int i=0; i<n; ++i) tmp[i]=a[i]; tmpn=n;
sort(a, a+n); n=unique(a, a+n)-a;
for (int i=0; i<n; ++i) //对a去重,保存重复个数
for (int j=0; j<tmpn; ++j) if (a[i]==tmp[j]) ++c[i];
for (LL i=0; i<n; ++i)
for (LL j=0; j<n; ++j)
if (a[i]!=a[j]) b[tl2++]=gcd(a[i], a[j]);
sort(b, b+tl2); tl2=unique(b, b+tl2)-b; //1和所有大于1e6且在至少两个数上出现的质数 tl2最多到2n
for (LL i=0; i<n; ++i) //把所有能分解的数都分解了,剩下的数,素因子一定只有自己有
for (LL j=0; j<tl2; ++j){
if (b[j]==1) continue;
if (a[i]%b[j]==0){
while (c[i]--) allp[tl++]=b[j],
allp[tl++]=a[i]/b[j];
a[i]=1;
}
}
for (LL i=0; i<n; ++i){
if (a[i]==1) continue;
if (sqr(sqrt(a[i]))==a[i]){ //如果A=a*a
a[i]=sqrt(a[i]);
while (c[i]--) allp[tl++]=a[i], allp[tl++]=a[i];
continue;
}
if (isprime(a[i])) ++appear[c[i]]; else appear[c[i]]+=2;
}
sort(allp, allp+tl); LL j=1, maxm=0;
for (LL i=1; i<tl; ++i, ++j){ //appear[j]:j次的素数有多少个
if (allp[i]!=allp[i-1]){
if (allp[i-1]!=1) ++appear[j];
j=0;
}
}
if (tl) ++appear[j];
for (maxm=maxn*60-1; maxm>=0; --maxm) if (appear[maxm]) break;
printf("%lld\n", maxm); x[0]=1;
for (LL i=0; i<appear[maxm]; ++i){
for (LL j=0; j<maxn*2; ++j) x[j]*=2;
for (LL j=0; j<maxn*2-1; ++j) x[j+1]+=x[j]/10, x[j]%=10;
} LL len;
for (len=maxn*2-1; len>=0; --len) if (x[len]) break;
--x[0];
for (LL i=0; i<maxn*2; ++i) if (x[i]<0) x[i]+=10, --x[i+1];
for (LL i=len; i>=0; --i) printf("%lld", x[i]);
//fclose(stdin); fclose(stdout);
return 0;
}
标签:个数 abi 因子 ext inline scan 相同 actor tin
原文地址:https://www.cnblogs.com/MyNameIsPc/p/9319328.html