[BZOJ 2082]POI 2010 Divine divisor

25 篇文章 0 订阅

数论小集合。

题目里第一问本质上要求的其实是 数a1*a2*a3*....*am(m<=600)分解质因数之后最大的指数k,第二问yy一下发现要求的就是  2^(指数是k的素数个数)-1

瓶颈在于我们如何求出这个质因数分解,ai<=10^18!

我们最多能找出ai在10^6(不到80000个素数)以内的质因子,我们就先找出小因子。

与下来只可能存在 p、p*q、p^2

p^2的情况可以很容易判定能够出来

我们又可以想到了大素数判定(笔者弱弱的不会写Miller-Rabin敲打挖个坑以后来填吧)。

那么最后只剩下p*q的情况。

我们先把m个数两两做gcd,如果不为1那么就能把这数分解了。如果仍是没有分出来,那么我们也没有必要知道p、q是多少,因为我们只关心他的指数是1就可以了。


贴上代码——又丑又长又慢。。。。抓狂

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
#define sqr(x) ( (int)(x)*(x) )
typedef long long LL;
const int Maxn=1000007;
LL x,xx,MD6,MD12,INF;
int n,tot,maxx,cnt,i,j,cb;
int prime[Maxn];
bool check[Maxn];
struct arr
{
  LL dig; int sum;
  bool operator <(const arr &x) const
    { return dig<x.dig; }
} A[Maxn],B[Maxn];

struct BIGINT
{
  int a[500],h;
  BIGINT operator +(const BIGINT &x)const
  {
  	BIGINT ret; int p=0;
  	memset(ret.a,0,sizeof(ret.a));
  	for (int i=1;i<=h;i++){
  	  ret.a[i]=a[i]+x.a[i]+p;
  	  p=ret.a[i]/10;
  	  ret.a[i]=ret.a[i]%10;
  	}
  	ret.h=h;
  	if (p>0) ret.a[++ret.h]=p;
  	return ret;
  }
} ANS;

void init(){
  for (i=2;i<Maxn;i++){
  	if (check[i]==0) prime[tot++]=i;
  	for (j=0;j<tot;j++){
  	  if (i*prime[j]>=Maxn) break;
  	  check[i*prime[j]]=1;
  	  if (i%prime[j]==0) break;
  	}
  }
}

LL mul(LL a,LL b,LL mod){
  LL ret=0;
  while (b){
  	if (b&1){
  	  ret=ret+a;
  	  if (ret>=mod) ret-=mod;
    }
    a<<=1; b>>=1;
    if (a>=mod) a-=mod;
  }
  return ret;
}

LL qck(LL a,LL b,LL mod){
  LL ret=1;
  for (;b>0;b>>=1){
  	if (b&1) ret = mul(ret,a,mod);
  	a = mul(a,a,mod);
  }
  return ret;
}

bool prime_check(LL x){
  for (int i=0;i<=5&&prime[i]<=x;i++)
    if (qck((LL)prime[i],x-1,x)!=1) return 0; 
  return 1;
}

void work1(){
  for (i=1;i<=n;i++){
    scanf("%I64d",&x);
    //printf("%I64d,",x);
    for (j=0,xx=x;x>1&&j<tot;j++){
      if (prime[j]>xx/prime[j]) break;
      
      if (xx%prime[j]) continue;
      A[cnt].dig=prime[j];
      while (xx%prime[j]==0) A[cnt].sum++, xx/=prime[j];
      cnt++;
    }
    if (xx>=Maxn){
      if (prime_check(xx)){
      	A[cnt].dig=xx;
      	A[cnt++].sum=1;
      } else
      {
      	if ( sqr(sqrt(xx))==xx ){
      	  A[cnt].dig=(int)sqrt(xx);
      	  A[cnt++].sum=2;
      	} else
      	{
      	  B[cb].dig=xx;
      	  B[cb++].sum=1;
      	}
      }
    } else
    if (xx>1) {
      A[cnt].dig=xx;
      A[cnt++].sum=1;
    }
  }
  //printf("\n");
}

LL gcd(LL a,LL b){
  while (b) b^=a^=b^=a%=b;
  return a;
}

void work2(){
  INF=1000000005; INF=INF*INF;
  MD6=1000000; MD12=MD6*MD6;
  
  sort(A,A+cnt);
  for (i=cnt-2;i>=0;i--)
  if (A[i].dig==A[i+1].dig){
  	A[i].sum+=A[i+1].sum;
  	A[i+1].dig=INF;
  }
  sort(A,A+cnt);
  for (i=cnt-1;i>=0;i--)
    if (A[i].dig>=INF) cnt--;
  
  //printf("%d\n",cnt);
  //for (i=0;i<cnt;i++)
  //  printf("%I64d^%d\n",A[i].dig,A[i].sum);
  
  sort(B,B+cb);
  for (i=cb-2;i>=0;i--)
  if (B[i].dig==B[i+1].dig){
  	B[i].sum+=B[i+1].sum;
  	B[i+1].dig=INF;
  }
  sort(B,B+cb);
  for (i=cb-1;i>=0;i--)
    if (B[i].dig>=INF) cb--;
  
  for (i=0;i<cb;i++){
  	
  	for (j=cnt-1;A[j].dig>MD6;j--){
  	  if (B[i].dig%A[j].dig==0)
  	    B[i].dig/=A[j].dig, A[j].sum+=B[i].sum;
  	}
  	
  	for (j=i+1;j<cb;j++)
	if (gcd(B[i].dig,B[j].dig)>1){
  	  A[cnt].dig=gcd(B[i].dig,B[j].dig);
  	  A[cnt].sum=0;
  	  if (B[i].dig%A[cnt].dig==0)
  	    B[i].dig/=A[cnt].dig, A[cnt].sum+=B[i].sum;
  	  cnt++;
  	}
  	
  	if (B[i].dig>=MD6){
  	  A[cnt].dig=INF;
  	  A[cnt++].sum=B[i].sum;
  	}
  	if (B[i].dig>=MD12){
  	  A[cnt].dig=INF;
  	  A[cnt++].sum=B[i].sum;
  	}
  }
  
  //printf("%d\n",cnt);
  //for (i=0;i<cnt;i++)
  //  printf("%I64d^%d\n",A[i].dig,A[i].sum);
  
  
  for (i=0;i<cnt;i++)
  	maxx=max(maxx,A[i].sum);
  printf("%d\n",maxx);
  ANS.h=1; ANS.a[1]=1;
  for (i=0;i<cnt;i++)
    if (A[i].sum==maxx)
      ANS = (ANS+ANS);
  ANS.a[1]--;
  for (i=ANS.h;i>0;i--)
  	printf("%d",ANS.a[i]);
  printf("\n");
}

int main(){
  init();
  scanf("%d",&n);
  work1();
  work2();
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值