BZOJ2082: [Poi2010]Divine divisor

111 篇文章 0 订阅
14 篇文章 0 订阅

题目大意:给出一个用m个ai乘起来表示的n,求出一个最大的k,使得存在一个d,使得d^k|n,并求出在这种情况下符合条件的d的个数


我们想求得答案,很自然的想要进行质因数分解,想要把n进行质因数分解,显然要把ai进行质因数分解

但是由于ai有点大,直接根号ai全部分解显然是不现实的

那么我们不妨考虑先筛出10^6以内的所有素数,然后用这些素数来筛ai

这样筛完之后,对于所有的ai,只可能有四种情况:

1.被筛干净变成了1,这样我们就算处理完了这个数

2.被筛成了一个大于10^6的大质数p,我们可以用Miller-Rabin来检验剩下的数是否属于这种情况

3.被筛成了一个大于10^6的大质数p的平方,我们可以直接开根来判断是否属于这种情况

4.被筛成了两个大于10^6的大质数p,q的乘积

由于10^6以内的素数我们全都筛过了,所以一定不会出现剩下的数包含三个可重质因子的情况


前三种情况我们可以非常(轻松)地解决,现在考虑最后一种情况

若存在一个大质数p在n中出现2次以上,则其必然至少在两个这种形态的数中出现(上面的情况暂时不考虑)

所以我们可以去重之后对剩下的ai两两做gcd,把gcd不为1的拿出来,这是可能出现多次的素数,剩下的大质数我们不需要知道他们具体是几,只需要知道有多少个这样的数,他们的出现次数均为1就好了

统计出n中每个质数出现了多少次后,我们可以轻松算出k,设质数出现次数=k的有x个,则第二问答案为2^x-1

这里可能会爆longlong,所以要用高精度


具体实现上可能会有一些细节问题,参见代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define max(x,y) ((x)>(y)?(x):(y))
#define N 1000010
using namespace std;
long long p[N],cnt;
bool he[N];
void getp()
{
	long long i,j;
	for(i=2;i<=1000000;i++)
	{
		if(!he[i]) cnt++,p[cnt]=i;
		for(j=1;j<=cnt&&i*p[j]<=1000000;j++)
		{
			he[i*p[j]]=true;
			if(i%p[j]==0) break;
		}
	}
}
long long a[610],v[610];
map<long long,long long>P;
map<long long,bool>Q[610];
long long gcd(long long x,long long y)
{
	if(x==0) return y;
	return gcd(y%x,x);
}
bool psqr(long long x)
{
	long long t=(int)sqrt(x);
	return t*t==x;
}
long long ran(long long n)
{
	return (((long long)rand()<<30)+rand())%n+1;
}
long long mul(long long a,long long b,long long p)
{
	long long tmp=a*b-(long long)((long double)a/p*b+1e-7)*p;
	return (tmp+p)%p;
}
long long ksm(long long d,long long c,long long mod)
{
	if(c==0) return 1;
	if(c==1) return d;
	if(c%2==0) return ksm(mul(d,d,mod),c/2,mod);
	else return mul(ksm(mul(d,d,mod),c/2,mod),d,mod);
}
bool MillerRabin(long long n,int T)
{
	long long r=n-1,s=0;
	if(n%2==0) return false;
	while(r%2==0)
	r/=2,s++;
	while(T--)
	{
		long long A=ran(n-3)+1,ss=s;
		A=ksm(A,r,n);
		long long la=A;
		while(ss--)
		{
			A=mul(A,A,n);
			if(A==1&&la!=n-1&&la!=1) return false;
			la=A;
		}
		if(A!=1) return false;
	}
	return true;
}
struct ppp
{
	int clong,a[610];
	ppp()
	{
		clong=1;
		memset(a,0,sizeof(a));
	}
	void jinwei()
	{
		int i;
		for(i=1;i<clong;i++)
		{
			a[i+1]+=a[i]/10;
			a[i]%=10;
		}
		while(a[clong]>=10)
		{
			clong++;
			a[clong]=a[clong-1]/10;
			a[clong-1]%=10;
		}
	}
	void print()
	{
		int i;
		for(i=clong;i>=1;i--)
		printf("%d",a[i]);
	}
};
ppp ret;
ppp operator *(ppp &x,int y)
{
	int i;
	ret.clong=x.clong;
	for(i=1;i<=x.clong;i++)
	ret.a[i]=2*x.a[i];
	ret.jinwei();
	return ret;
}
ppp ANS;
int main()
{
	getp();
	long long n;
	scanf("%lld",&n);
	long long i,j;
	long long x,y;
	for(i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		j=1;
		while(a[i]!=1&&j<=cnt)
		{
			while(a[i]%p[j]==0)
			{
				a[i]/=p[j];
				P[p[j]]++;
			}
			j++;
		}
	}
	sort(a+1,a+n+1);
	for(i=1;i<n;i++)
	{
		if(a[i+1]==a[i])
		{
			v[i]++;
			for(j=i+1;j<n;j++)
			a[j]=a[j+1];
			n--;i--;
		}
	}
	for(i=1;i<=n;i++)
	v[i]++;
	for(i=1;i<=n;i++)
	for(j=i+1;j<=n;j++)
	{
		x=gcd(a[i],a[j]);
		if(x!=1) Q[i][x]=Q[j][x]=true;
	}
	long long maxn=1,ans=0;
	map<long long,bool>::iterator it;
	for(i=1;i<=n;i++)
	if(a[i]!=1)
	{
		if(psqr(a[i]))
		P[(long long)sqrt(a[i])]+=v[i]*2;
		else
		{
			if(Q[i].size()==2)
			{
				it=Q[i].begin();
				P[it->first]+=v[i];
				it++;
				P[it->first]+=v[i];
			}
			else if(Q[i].size()==1)
			{
				it=Q[i].begin();
				P[it->first]+=v[i];
				if(it->first!=a[i])
				{
					if(v[i]>maxn) maxn=v[i],ans=1;
					else if(v[i]==maxn) ans++;
				}
			}
			else if(MillerRabin(a[i],10))
			{
				if(v[i]>maxn) maxn=v[i],ans=1;
				else if(v[i]==maxn) ans++;
			}
			else
			{
				if(v[i]>maxn) maxn=v[i],ans=2;
				else if(v[i]==maxn) ans+=2;
			}
		}
	}
	map<long long,long long>::iterator IT;
	for(IT=P.begin();IT!=P.end();IT++)
	{
		if(IT->second==maxn) ans++;
		else if(IT->second>maxn) maxn=IT->second,ans=1;
	}
	printf("%lld\n",maxn);
	ANS.a[1]=1;
	for(i=1;i<=ans;i++)
	ANS=ANS*2;
	ANS.a[1]--;
	ANS.print();
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值