HDOJ-4344 Mark the Rope(数论)

概括一下题意:将一个合数n的因数(不包括1和n)看做一个集合,求一个最大的子集使集合内所有元素都互质,并求出在上述条件下集合元素最大的和是多少。

解法:既然要保证子集内的元素互质,就要保证任意两个元素没有质因子。要元素数目做多,则每个元素包含的质因子数目最少,即为1。

做法也就出来了,对n进行拆分质因数,表示成 n=(a1^p1)*(a2^p2)*(a3^p3)*......*(ak^pk),则子集大小就是k,子集所有元素的最大的和就是(a1^p1)+(a2^p2)+(a3^p3)+......+(ak^pk)。当然,有一种特殊情况,就是n只有一种质因数,n=a^p,例如16,9等等,因为n不在大的集合之内,所以sum=a^(p-1)

现在转化为了大数快速分解质因数的问题,可以使用神奇的“pollard-rho”算法

传送门:http://www.cnblogs.com/jackiesteed/articles/2019910.html

在pollard-rho算法中用到了Miller-Rabin素数检验

传送门:http://www.matrix67.com/blog/archives/234

如果感觉自己很难理解,还是老老实实套模板吧:

(kuangbin模板)


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;


/*
Miller_Rabin 算法判断是否为素数
*/
const int S=8;

//ret=(a*b)%c;
LL mult_mod(LL a,LL b,LL c)
{
	a %= c;
	b %= c;
	LL ret=0;
	LL tmp=a;
	while(b)
	{
		if(b&1)
		{
			ret+=tmp;
			if(ret>c) ret-=c;
		}
		tmp<<=1;
		if(tmp>c) tmp-=c;
		b>>=1;
	}
	return ret;
}

//res=x^n %mod;
LL pow_mod(LL x,LL n,LL mod)
{
    LL t,res;
    res=1;
	t=x;
    while(n)
    {
        if(n&1)
			res=mult_mod(res,t,mod);
        n>>=1;
        t=mult_mod(t,t,mod);
    }
    return res;
}

//是合数返回1,不一定是合数返回0
bool check(LL a, LL n, LL x, LL t)
{
	int i;
	LL ret=pow_mod(a,x,n);
	LL last=ret;
	for(i=1;i<=t;i++)
	{
		ret=mult_mod(ret,ret,n);
		if(ret==1 && last!=1 && last!=n-1) return 1;
		last=ret;
	}
	if(ret!=1)
		return 1;
	else
		return 0;
}

//很可能是素数返回1,一定不是素数返回0
bool Miller_Rabin(LL n)
{
	int i;
	if (n<2)      return 0;
	if (n==2)     return 1;
	if ((n&1)==0) return 0;
	LL x=n-1;
	LL t=0;
	while((x&1)==0)
	{
		x>>=1;
		t++;
	}
	srand(time(NULL));
	for(i=0;i<S;i++)
	{
		LL a=rand()%(n-1)+1;
		if(check(a,n,x,t))
			return 0;
	}
	return 1;
}

/*
pollard_rho算法进行质因数分解
*/

LL factor[1000];
int tol;//质因数个数
LL gcd(LL a,LL b)
{
	LL t;
	while(b)
	{
		t=a;
		a=b;
		b=t%b;
	}
	if(a>=0) return a;
		else return -a;
}

LL pollard_rho(LL x,LL c)
{
	LL i=1,k=2;
	srand(time(NULL));
	LL x0=rand()%(x-1)+1;
	LL y=x0;
	while(1)
	{
		i++;
		x0=(mult_mod(x0,x0,x)+c)%x;
		LL d=gcd(y-x0,x);
		if(d!=1 && d!=x) return d;
		if(y==x0) return x;
		if(i==k)
		{
			y=x0;
			k+=k;
		}
	}
}

void findfac(LL n,int k)
{
	if(n==1) return ;
	if(Miller_Rabin(n))
	{
		factor[tol++]=n;
		return ;
	}
	LL p=n;
	int c=k;
	while(p>=n)
		p=pollard_rho(p,c--);
	findfac(p,k);
	findfac(n/p,k);
}

void work()
{
	int i;
	LL n,ans,tmp,cnt;
	scanf("%I64d",&n);
	tol=0;
	findfac(n,107);
	sort(factor,factor+tol);

	ans=0;
	tmp=factor[0];
	cnt=1;
	for(i=1;i<tol;i++)
	{
		if(factor[i]!=factor[i-1])
		{
			cnt++;
			ans+=tmp;
			tmp=factor[i];
		}
		else
		{
			tmp*=factor[i];
		}
	}
	ans+=tmp;
	if(cnt==1) ans/=factor[0];
	printf("%I64d %I64d\n",cnt,ans);
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
		work();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值