BZOJ2082 : [Poi2010]Divine divisor

本文介绍了一种高效的质因数分解算法,适用于处理大量整数的质因数分解任务。该算法通过预处理找到小于10^6的所有质数,并针对大于10^6的因子采用Miller-Rabin素性测试等方法进行高效处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

将所有数分解质因数,那么第一问就是求指数的最大值,第二问就是$2^{指数最大的质数个数}-1$。

首先将$10^6$以内的质因数全部找到,那么剩下部分的因子$>10^6$,且只有3种情况:

1.大质数

2.大质数的平方

3.两个大质数的乘积

对于1可以用MillerRabin算法判定,对于2可以尝试开根号然后判定。

那么剩下的一定是3,对于每个不确定的数字,如果它所含的因子只有它有,那么这两个因子可以合并,算第二问的时候个数$+=2$即可。

判断其它数字是否也有这个因子,只需要求gcd即可。

时间复杂度$O(\frac{nv^{\frac{1}{3}}}{\ln v})$。

 

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int N=1000000,B=10000,MAXL=220;
int n,i,j,k,tot,p[N],v[N],ca,cb,cc,ans0,ans1;ll a[64*600],b[610],c[610];
inline ll gcd(ll a,ll b){
  ll c=1;
  while(a-b){
    if(a&1){
      if(b&1){
        if(a>b)a=(a-b)>>1;else b=(b-a)>>1;
      }else b>>=1;
    }else{
      if(b&1)a>>=1;else c<<=1,a>>=1,b>>=1;
    }
  }
  return c*a;
}
inline ll mul(ll a,ll b,ll n){return(a*b-(ll)(a/(long double)n*b+1e-3)*n+n)%n;}
inline ll pow(ll a,ll b,ll n){
  ll d=1;a%=n;
  while(b){
    if(b&1)d=mul(d,a,n);
    a=mul(a,a,n);
    b>>=1;
  }
  return d;
}
inline bool check(ll a,ll n){
  ll m=n-1,x,y;int i,j=0;
  while(!(m&1))m>>=1,j++;
  x=pow(a,m,n);
  for(i=1;i<=j;x=y,i++){
    y=pow(x,2,n);
    if((y==1)&&(x!=1)&&(x!=n-1))return 1;
  }
  return y!=1;
}
inline bool miller_rabin(ll n){
  int t=5;ll a;
  if(!(n&1))return 0;
  while(t--)if(check(rand()%(n-1)+1,n))return 0;
  return 1;
}
inline ll getsqrt(ll n){
  ll x=sqrt(n);
  for(ll i=x-2;i<=x+2;i++)if(i*i==n)return i;
  return 0;
}
inline void divide(){
  ll n;
  scanf("%lld",&n);
  for(int i=0;i<tot&&p[i]<=n;i++)while(n%p[i]==0)n/=p[i],a[++ca]=p[i];
  if(n==1)return;
  if(miller_rabin(n)){a[++ca]=c[++cc]=n;return;}
  ll t=getsqrt(n);
  if(t){a[++ca]=t;a[++ca]=c[++cc]=t;return;}
  b[++cb]=n;
}
inline void solve(ll n){
  for(int i=1;i<=cc;i++)if(n%c[i]==0){
    a[++ca]=c[i],a[++ca]=n/c[i];
    return;
  }
  for(int i=1;i<=cb;i++){
    ll t=gcd(n,b[i]);
    if(t==1||t==n)continue;
    a[++ca]=t,a[++ca]=n/t;
    return;
  }
  a[++ca]=-n;
}
struct Num{
  int a[MAXL],len,fu;
  Num(){len=1,fu=a[1]=0;}
  Num operator+(const Num&b){
    Num c;
    c.len=max(len,b.len)+2;
    int i;
    for(i=1;i<=c.len;i++)c.a[i]=0;
    if(fu==b.fu){
      for(i=1;i<=len;i++)c.a[i]=a[i];
      for(i=1;i<=b.len;i++)c.a[i]+=b.a[i];
      for(i=1;i<=c.len;i++)if(c.a[i]>=B)c.a[i+1]++,c.a[i]-=B;
      while(c.len>1&&!c.a[c.len])c.len--;
      c.fu=fu;
    }else{
      bool flag=0;
      if(len==b.len){
        for(i=len;i;i--)if(a[i]!=b.a[i]){
          if(a[i]>b.a[i])flag=1;
          break;
        }
      }else{
        if(len>b.len)flag=1;
      }
      if(flag){
        for(i=1;i<=len;i++)c.a[i]=a[i];
        for(i=1;i<=b.len;i++)c.a[i]-=b.a[i];
        for(i=1;i<=c.len;i++)if(c.a[i]<0)c.a[i+1]--,c.a[i]+=B;
        while(c.len>1&&!c.a[c.len])c.len--;
        c.fu=fu;
      }else{
        for(i=1;i<=b.len;i++)c.a[i]=b.a[i];
        for(i=1;i<=len;i++)c.a[i]-=a[i];
        for(i=1;i<=c.len;i++)if(c.a[i]<0)c.a[i+1]--,c.a[i]+=B;
        while(c.len>1&&!c.a[c.len])c.len--;
        c.fu=b.fu;
      }
    }
    return c;
  }
  void write(){
    printf("%d",a[len]);
    for(int i=len-1;i;i--)printf("%04d",a[i]);
  }
  void set(int x){
    if(x<0){a[len=1]=fu=1;return;}
    fu=0,a[len=1]=x;
  }
}num,sub;
int main(){
  for(i=2;i<N;i++){
    if(!v[i])p[tot++]=i;
    for(j=0;j<tot&&i*p[j]<N;j++){
      v[i*p[j]]=1;
      if(i%p[j]==0)break;
    }
  }
  scanf("%d",&n);
  while(n--)divide();
  for(i=1;i<=cb;i++)solve(b[i]);
  sort(a+1,a+ca+1);
  for(i=1;i<=ca;i=j){
    for(j=i;j<=ca&&a[i]==a[j];j++);
    k=j-i,tot=a[i]<0?2:1;
    if(k>ans0)ans0=k,ans1=tot;else if(k==ans0)ans1+=tot;
  }
  printf("%d\n",ans0);
  num.set(1);
  while(ans1--)num=num+num;
  sub.set(-1);
  num=num+sub;
  num.write();
  return 0;
}

  

资源下载链接为: https://pan.quark.cn/s/9e7ef05254f8 行列式是线性代数的核心概念,在求解线性方程组、分析矩阵特性以及几何计算中都极为关键。本教程将讲解如何用C++实现行列式的计算,重点在于如何输出分数形式的结果。 行列式定义如下:对于n阶方阵A=(a_ij),其行列式由主对角线元素的乘积,按行或列的奇偶性赋予正负号后求和得到,记作det(A)。例如,2×2矩阵的行列式为det(A)=a11×a22-a12×a21,而更高阶矩阵的行列式可通过Laplace展开或Sarrus规则递归计算。 在C++中实现行列式计算时,首先需定义矩阵类或结构体,用二维数组存储矩阵元素,并实现初始化、加法、乘法、转置等操作。为支持分数形式输出,需引入分数类,包含分子和分母两个整数,并提供与整数、浮点数的转换以及加、减、乘、除等运算。C++中可借助std::pair表示分数,或自定义结构体并重载运算符。 计算行列式的函数实现上,3×3及以下矩阵可直接按定义计算,更大矩阵可采用Laplace展开或高斯 - 约旦消元法。Laplace展开是沿某行或列展开,将矩阵分解为多个小矩阵的行列式乘积,再递归计算。在处理分数输出时,需注意避免无限循环和除零错误,如在分数运算前先约简,确保分子分母互质,且所有计算基于整数进行,最后再转为浮点数,以避免浮点数误差。 为提升代码可读性和可维护性,建议采用面向对象编程,将矩阵类和分数类封装,每个类有明确功能和接口,便于后续扩展如矩阵求逆、计算特征值等功能。 总结C++实现行列式计算的关键步骤:一是定义矩阵类和分数类;二是实现矩阵基本操作;三是设计行列式计算函数;四是用分数类处理精确计算;五是编写测试用例验证程序正确性。通过这些步骤,可构建一个高效准确的行列式计算程序,支持分数形式计算,为C++编程和线性代数应用奠定基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值