2019牛客暑假多校第三场 D Big Interger

 题目大意:给一个序列A(x),A(i)代表由i个一组成的十进制数,A(3)=111.A(5)=11111等,给定素数p,求所有能满足A(i^j)%p==0的(i,j)有多少对。

解:

首先一个显然的结论:A(p-1)%p==0,p是质数。

我们要统计(i,j)有多少对等价于找出x从小到大第一个使得A(x)%p==0的x,然后找多少对(i,j)满足x<=i^j。

首先一个结论,对于p>=5,p是素数,那么A(p-1)%p==0.

这意味着我们只需要从p-1的因子中找那个x就可以了,要探寻的范围小了很多。

于是因数分解,质因数分解,再从小到大暴力枚举因数,找到x(因为A(x)=(10^x-1)/9,所以直接处理逆元再快速幂就可以了)

找到了这样的x,怎么计数对数呢

还是暴力枚举,不过需要几个技巧:

1.x=p1^q1*p2^q2*……*pn^qn,开1/j次根号,相当于对qi/j向上取整,所以枚举j,对于每个j值算出来的temp,ans+=n/temp;

2.找出最大的qi,记为mx,j的枚举次数应该小于min(mx,m),因为当j大于mx时,pow(qi,1/j)向上取整等于1,不必再往后枚举了,直接计算。

#include 
#include 
#include
#include 
using namespace std;
typedef long long ll;
typedef int INT;
#define int \
long long
const int maxn=50050;
//const ll MOD=1e9+7;
ll divv[maxn];
ll prime[maxn];
ll p,n,m;
int sum[maxn];
int cnt,tot;
 
void divide(ll x){
    cnt=0;
    for(ll i=1;i*i<=x;i++){
        if(x%i==0){
            divv[++cnt]=i;
            if(i!=x/i)  divv[++cnt]=x/i;
        }
    }
}
void get_prime(ll x){
    tot=0;
    memset(sum,0,sizeof(sum));
    for(ll i=2;i*i<=x;i++){
        if(x%i==0){
            prime[++tot]=i;
            while(x%i==0){
                x/=i;sum[tot]++;
            }
        }
    }
    if(x>1){
        prime[++tot]=x;sum[tot]=1;
    }
}
ll quickpow(ll a,ll b,ll MOD){
    ll c=1;
    while(b){
        if(b&1) c=c*a%MOD;
        a=a*a%MOD;b>>=1;
    }
    return c;
}
ll inv(ll a,ll MOD){
    return quickpow(a,MOD-2,MOD);
}
void solve(){
    int T;
    scanf("%d",&T);
    while(T--){
        //memset()
        scanf("%lld %lld %lld",&p,&n,&m);
        if(p==2||p==5)  printf("0\n");
        else if(p==3)   printf("%lld\n",n/3*m);
        else{
            //if(p==3) p++;
            divide(p-1);
            //for(int i=0;i<=cnt;i++) printf("divv[%d]=%lld\n",i,divv[i]);
            sort(divv+1,divv+cnt+1);
            ll res=0;
            for(int i=2;i<=cnt;i++){
                //printf("i=%d,qp=%lld\n",i,((quickpow(10,divv[i],p)-1+p)%p*inv(9,p)%p)%p);
                if((quickpow(10,divv[i],p)-1+p)%p*inv(9,p)%p==0){
                    res=divv[i];break;
                }
            }
            get_prime(res);
            ll ans=0;
            int mx=-1;
            for (int i=1;i<=tot;i++) mx=max(mx,sum[i]);
            for(int j=1;j<=min(m,mx);j++){
                ll temp=1;
                for(int i=1;i<=tot;i++){
                    ll s=(sum[i]+j-1)/j;
                    for (int k=0;kmx) {
                ll ret=1;
                for (int j=1;j<=tot;j++) ret=ret*prime[j];
                ans+=(m-mx)*(n/ret);
            }
            printf("%lld\n",ans);
        }
    }
}
INT main(){
    solve();
    return 0;
}

 

你可能感兴趣的:(acm)