三校联考模拟赛(二)sum

三句话题面:
1.定义y|x,表示y整除x
2.且f(x)=y,表示x的正因子个数为y。(其实是$ \tau(x)=y 或 者 或者 \sigma_0(x)=y$)
计算同时满足两个条件的x,输出他们的累加和(取模),如果有无限多个输出-1。

一道非常毒瘤的题目。我们可以注意到将x和y进行质因数分解,通过打表和仔细观察,我们可以得到一个神奇的结论:如果y某个质因数指数>1,则x一定为-1(特殊的,4不是-1),然后我们利用公式 τ ( x ) = Π ( e i + 1 ) \tau(x)=\Pi(ei+1) τ(x)=Πei+1 ei为分解质因数后的第i个质数的指数。考虑用dfs遍历全排列就可以了。当然dp才可以防止被毒瘤数据卡住。(不同的质数太多)。

然而数据有一点小小的掉节操,1018次方,迫使你认为欧拉筛过不了。(然而有信仰的rym大佬只筛了107以内的数,,然后就过了。。。)
正确做法是使用Miller_Rabin算法判质数,Pollard_Rho算法分解因数。关于伪质数天敌carmichael,我们可以暴力判3 5 7 11 13 17 41的倍数,或者打表(只有255个数)。哦,还有特判1。

code:

#include<bits/stdc++.h>
#define N 20005
#define LL long long
using namespace std;
#define INF 2e18
#define TIMES 15
LL f[N],n,mo,T;
LL divsor[100];
int dcnt=0;
LL dmi=INF;
LL GetRandom(LL n)
{
    LL num = (((unsigned LL)rand() + 100000007)*rand())%n;
    return num+1;
}
LL Mod_Mul(LL a,LL b,LL mod)
{
    LL msum=0;
    while(b)
    {
        if(b&1) msum = (msum+a)%mod;
        b>>=1;
        a = (a+a)%mod;
    }
    return msum;
}
LL Quk_Mul(LL a,LL b,LL mod)
{
    LL qsum=1;
    while(b)
    {
        if(b&1) qsum=Mod_Mul(qsum,a,mod);
        b>>=1;
        a=Mod_Mul(a,a,mod);
    }
    return qsum;
}
bool Miller_Rabin(LL n)
{
    if(n==2||n==3||n==5||n==7||n==11||n==13||n==17||n==41) return true;
    if(n==1||n%2==0||n%3==0||n%5==0||n%7==0||n%11==0||n%41==0||n%17==0) return false;
    if (n%13==0) return false;
    int div2=0;
    LL tn=n-1;
    while( !(tn&1) )
    {
        div2++;
        tn>>=1;//脱平方根 
    }
    for(int tt=0;tt<TIMES;tt++)
    {
        LL x=GetRandom(n-1); //随机得到[1,n-1]
        if(x==1) continue;
        x=Quk_Mul(x,tn,n);
        LL pre=x;//平凡平方根,最小根可以为p-1,其余必须为1
        for(int j=0;j<div2;j++)
        {
            x = Mod_Mul(x, x, n);
            if(x==1&&pre!=1&&pre!=n-1) return false;
            pre=x;
        }
        if(x!=1) return false;
    }
    return true;
}
LL gcd(LL a,LL b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}
LL pollard_rho(LL dn,LL dc)
{
    LL x,y,d,i=1,k=2;
    x=GetRandom(dn-1);
    y=x;
    while(1)
    {
        i++;
        x=(Mod_Mul(x,x,dn)+dc)%dn;
        d=gcd(y-x,dn);
        if(1<d&&d<dn)
            return d;
        if(y==x) return dn;
        if(i==k)
        {
            y=x;
            k<<=1;
        }
    }
}
void Divide(LL dn,int dk){
	if(dn==1)return ;
	if(Miller_Rabin(dn)==true){
		divsor[dcnt++]=dn;
		dmi=min(dmi,dn);
		return ;
	}
	LL dtmp=dn;
	while(dtmp>=dn)dtmp=pollard_rho(dtmp,dk--);
	Divide(dtmp,dk);
	Divide(dn/dtmp,dk);
}
void solve(LL x,LL mo){
	if(x==1){printf("1\n");return ;}
	if(x==4) {printf("%lld",8%mo);return ;}
	memset(divsor,0,sizeof divsor); dcnt=0;
	if(Miller_Rabin(n))divsor[++dcnt]=n;
	else{dcnt++;Divide(x,251);}
	while(divsor[dcnt]==0)dcnt--;
	sort(divsor+1,divsor+dcnt+1);
	for(int i=1;i<=dcnt;i++)
		if(divsor[i]==divsor[i-1]){
			printf("-1\n");return ;
		}
	f[0]=1;
	for(int i=1;i<(1<<dcnt);i++){
		int s=0;f[i]=0;
		for(int j=i;j;j-=(j&-j),s++);
		for(int j=1;j<=dcnt;j++)
			if(i&(1<<(j-1))) f[i]=(f[i]+Mod_Mul(f[i-(1<<(j-1))],Quk_Mul(divsor[s],divsor[j]-1,mo),mo))%mo;
	}
	printf("%lld\n",f[(1<<dcnt)-1]);
	return ;
}
int main(){
	freopen("sum.in","r",stdin);
	freopen("sum.out","w",stdout);
	srand(unsigned(time(0)));
	scanf("%lld",&T);
	while(T--){
		scanf("%lld%lld",&n,&mo);
		solve(n,mo);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值