2019牛客暑期多校训练营(第三场) D Big Integer(数论)

题目链接:https://ac.nowcoder.com/acm/contest/883/D

 

题目大意:定义f(n)=n个1组成的十进制数,如f(3)=111,给出n和m,求1~n中取i,1~m中取j,f(i^{j})\equiv 0(mod p)有几对

 

题目思路:首先当p为2和5的时候可以发现无论如何它都没法mod p得到0,所以直接输出0就好。

为书写方便,这里定义n=i^{j}

f(n)=\frac{10^{n}-1}{9}

所以原式等价于求\frac{10^{n}-1}{9}=0(mod p)

=(10^{n}-1)*inv(9)=0(modp)

在p为3的情况下,由于3和9不互质,9没有逆元,所以3需要另外算,3的倍数就是所有位上的和需要是3的倍数,由于本题每一位都是3,所以就是求i^{j}是3的倍数的种类数,那就要求i是3的倍数,这种i共有n/3种,每种都可以跟任意一个j合作符合要求,也就是n/3*m

其他情况继续讨论,由于inv(9)不等于0

所以10^{n}-1=0(mod)p

10^{n}=1(mod p)

2和5已经刚才讨论过,剩下的全是跟10互质的p

那么由费马小定理or欧拉定理均可以得知

10^{p-1}=1(modp)

也就是循环节最大也就p-1了,如果想变小的话,很明显得要是它的因数,所以直接sqrt(n)枚举因数看看最小的循环节是谁。

找到最小循环节以后,假设它是x,那么它分解质因数以后得到的会是x=p_{1}^{a_{1}}p_{2}^{a_{2}}p_{3}^{a_{3}}...

对于最小的质数2,当指数为30时就已经超过了1e9,所以a不会大于30,如果大于的话直接当30算就行,因为只要temp拥有x的每一个质因子且数量大于等于x的相应数量就行。

所以对于每个j来说,最小的那个符合要求的数字得拥有x的每一个质因子且数量大于等于x的相应数量,直接a1/j向上取整就行,然后找出n里有多少个这种符合要求的数字的倍数,本题就结束了

 

以下是代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
const ll MAXN = 5e5+5;
const ll MOD = 1e9+7;
ll powmod(ll x,ll y,ll MOD){
    ll rst=1;
    for(;y;y>>=1){
        if(y&1)rst=rst*x%MOD;
        x=x*x%MOD;
    }
    return rst;
}
ll a[MAXN],num[MAXN];
int main()
{
    ll t,p,n,m;
    scanf("%lld",&t);
    while(t--){
        memset(num,0,sizeof(num));
        scanf("%lld%lld%lld",&p,&n,&m);
        if(p==2||p==5){cout<<0<<endl;continue;}
        if(p==3){cout<<n/3*m<<endl;continue;}
        ll temp=p-1,anss=p-1,pos=0;
        for(ll i=2;i<=sqrt(temp);i++){
            if(temp%i==0){
                if(powmod(10,i,p)==1){
                    anss=min(anss,i);
                }
                if(powmod(10,temp/i,p)==1){
                    anss=min(anss,temp/i);
                }
            }
        }
        rep(i,2,sqrt(anss)){
            if(anss%i==0){
                a[++pos]=i;
                while(anss%i==0)num[pos]++,anss/=i;
            }
        }
        if(anss!=1){
            a[++pos]=anss;
            num[pos]=1;
        }
        ll ans=0;
        rep(j,1,min(30ll,m)){
            ll rst=1;
            rep(i,1,pos){
                rst*=powmod(a[i],ceil(1.0*num[i]/j),p);
            }
            ans+=n/rst;
            if(j==30)ans+=n/rst*(m-30);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值