POI2010 题解整理
题目大意
给定一个n,找到n中出现次数最多的那些因子,并输出个数。(即要求找出满足 n%ax=0 , x 最大的a的个数)。
Input
- 第一行给出一个整数 m(1≤m≤600)
- 第二行是由空格分开的 m 个数 ai(2≤ai≤1018) , n 就是 ai 的乘积 (n=a1×a2×a3×⋯×am) 。
Sample Input
3
4 3 41
6Output
- 第一行为最大的 k 。
- 第二行为非凡因子 d 的个数。
Sample Output
4
11
3
感谢Matrix67大犇对Miller_Rabin的讲解,非常详细,一看就懂。
Solution :
总的来说这题的思路其实非常简单,而代码只是比较复杂而已。如果每个 ai 的数据范围小一点(至少 ai−−√ 可以被筛表覆盖),我们显然是考虑将所有的数全部进行分解,将所有因子统计归类,然后全部扫一遍就可以做了。此时有一个最基本不过的结论:
当范围提升到 1018 后,筛出 106 以上的素数便显得有些吃力无用了。根据上面那个结论,我们同样有以下结论:
那么我们有以下这个算法:
接下来就是考虑如何完成这个算法了。
首先就是如何判断这个数是一个大素数,还是两个素数相乘。这里就需要采用经典的Miller_Rabin算法进行判断。由于这个范围已经达到 1018 ,所以还要考虑用快速乘来避免long long溢出。
接下来重点处理p*q的情况,这个可以进行两两gcd拆分,此时还会出现无论如何也无法将这两个拆开的情况,这个时候就把这个整体当素数维护,统计个数时算两份就可以了。
最后的计算结果是:
#include
#define M 1000005
#define N 605
using namespace std;
typedef long long ll;
//预处理与调试部分
inline void Rd(ll &ans){
ans=0;char c;
while(c=getchar(),c<48);
do ans=(ans<<3)+(ans<<1)+(c^48);
while(c=getchar(),c>47);
}
long long res[N];
bool vis[M];
mapint >Mp;
int Map[M],mtop=0,ex=0;
void init(){
for(int i=2;iif(!vis[i]){
Map[++mtop]=i;
for(int j=i+i;j1;
}
}
void debug(){
mapint >::iterator it=Mp.begin();
for(;it!=Mp.end();++it)//if(it->second)
cout<<"first="<first<<','<<"second="<second<//素性测试:Miller_Rabin
const int prime[]={2,3,7,61,21251};
ll mod(ll m,ll n,ll P){
ll ans=m+n;
if(ans>=P)ans-=P;
return ans;
}//加法,带取模
ll mul(ll m,ll n,ll P){
if(m0;
while(n){
if(n&1)ans=mod(ans,m,P);
n>>=1;m=mod(m,m,P);
}
return ans;
}//快速乘
ll pow(ll m,ll n,ll P){
ll ans=1;
while(n){
if(n&1)ans=mul(ans,m,P);
n>>=1;m=mul(m,m,P);
}return ans;
}//快速幂
bool Miller_Rabin(ll pri,ll n){//只有a^d%n==1 或者 a^{d*2^i}%n==n-1
ll d=n-1;
while(d%2==0)d>>=1;
ll t=pow(pri,d,n);
while(d!=n-1&&t!=1&&t!=n-1)
t=mul(t,t,n),d<<=1;//当t==1时,上面的所有结果必然都是t==1
return (t==n-1)||(d&1);
}
bool judge(ll n){
for(int i=0;i<5;i++)
if(!Miller_Rabin(1LL*prime[i],n))return false;
return true;
}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
set Twoprime;
//高精度部分
struct BigInt{
static const int P=10000;
int len,num[1005];
BigInt(){
len=1;
memset(num,0,sizeof(num));
}
BigInt operator * (const BigInt &A)const{
BigInt B;
B.len=len+A.len-1;
for(int i=0;ifor(int j=0;jif(B.num[i+j]>=P){
B.num[i+j+1]+=B.num[i+j]/P;
B.num[i+j]%=P;
}
}
if(B.num[B.len])B.len++;
return B;
}
void Print(){
printf("%d",num[len-1]);
for(int i=len-2;i>=0;i--)printf("%04d",num[i]);
puts("");
}
};
BigInt Pow(BigInt n,ll m){
BigInt Ans;
Ans.num[0]=1;
while(m){
if(m&1)Ans=Ans*n;
m>>=1;n=n*n;
}
return Ans;
}
int main(){
init();int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)Rd(res[i]);
for(int i=1;i<=n;i++){
for(int j=1;Map[j]<=res[i]&&j<=mtop;j++)
while(res[i]%Map[j]==0){
res[i]/=Map[j];
Mp[Map[j]]++;
}
if(res[i]==1)continue;
ll p=sqrt(res[i])+0.01;
if(p*p==res[i]){res[i]=1,Mp[p]+=2;continue;}
if(judge(res[i]))Mp[res[i]]++,res[i]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;res[i]!=1&&j<=n;j++){
if(i==j||res[j]==1)continue;
ll c=gcd(res[i],res[j]);
if(c!=1&&c!=res[i])Mp[c]+=0,Mp[res[i]/c]+=0;
}
for(int i=1;i<=n;i++)
if(res[i]!=1){
mapint >::iterator it=Mp.begin();
for(;it->first<=res[i]&&it!=Mp.end();++it)
while(res[i]%it->first==0){
res[i]/=it->first;
it->second++;
}
if(res[i]!=1){
Twoprime.insert(res[i]);
Mp[res[i]]++;
res[i]=1;
}
}
ll ans=0,cnt=0;
mapint >::iterator it=Mp.begin();
while(it!=Mp.end()){
if(anssecond)ans=it->second,cnt=0;
if(ans==it->second){
if(Twoprime.find(it->first)!=Twoprime.end())cnt+=2;
else cnt++;
}++it;
}
BigInt Ans,Base;Base.num[0]=2;
Ans=Pow(Base,cnt);Ans.num[0]--;
cout<