2019牛客多校第三场D-Big Integer

题意

定义\(A(n)\) 为 n个1表示的十进制数,例如\(A(3) = 111\)

然后对于\(1 \le i \le n,1\le j \le m\) 问有多少的 \(pairs(i,j)\)满足\(A(i^j) \equiv 0 \pmod p\)

分析

$11\cdots 111 = {10^n-1 \over 9} \equiv 0\pmod p $

等价于\(10^n \equiv 1\pmod {9p}\)

\(p = 2,5\)时,显然答案为0(因为\(11\cdots 111\) 模2或模5肯定不是0)

\(p \neq 2,5\) 时,有\(gcd(10,9p) = 1\),有\(10^{\phi(9p)} \equiv 1\pmod {9p}\)

\(\phi(9p)\)是欧拉函数,这个式子由欧拉定理可知

所以只需要找\(10^i mod ~9p\) 的最小循环节d,显然 \(d|\phi(9p)\) ,所以只需要暴力找\(\phi(9p)\)的因子,找到最小的符合条件的即可

”显然成立“部分证明:

设d不是 \(n = \phi(9p)\) 的因子,那么 \(n = kd + r\) , 又\(10^{n} \equiv 1\pmod {9p}\) ,\(10^{d} \equiv 1\pmod {9p}\) ,所以有\(10^r \equiv 1\pmod {9p}\),r比d小,与d最小矛盾

接下来只需要找有多少个\(pair(i,j), d|i^j\)

把 d 质因数分解:\(d=p_1^{k_1}p_2^{k_2}\cdots p_l^{k_l}\), 要使得 \(i^j\)\(d\) 的倍数,那么在 \(i^j\) 的质因数分解中 \(p_1,p_2\cdots p_l\) 的指数中都要比\(d\) 中的大,所以我们考虑 j 固定的时候,有多少个 i 可以满足条件。

很容易就可以想到

\(i\) 必须是 \(g = p_1^{\lceil {k_1\over j} \rceil} p_2^{\lceil {k_2\over j} \rceil}\cdots p_l^{\lceil {k_l\over j} \rceil}\) 的倍数(至于为什么上取整,可以想一想,因为要求最小的 \(x\), 有\(x*j >= k_1 \&\& x*(j-1) < k_1\) )。因此一共有\(n\over g\)个合法的\(i\)

\(mx = max(k_1,k_2,\cdots k_l)\) 那么 我们只需要依次计算\(j,(j \in [1,mx])\) 就可以了。而对于 \(j > mx\) 的部分,合法的 i 的个数都是一样的。不妨带入上式看一看。

计算原理就是这样。但是实际操作又遇到了一些坑..

计算\(\phi(9p)\) 后,枚举因数找循环节时,快速幂会爆ll,所以要用快速乘(因为p最大1e9)

另一种方法是,因为欧拉函数是积性函数,所以如果9和p互质,那么\(\phi(9p) = phi(9) *(p-1)\) ,对\(p=3\)的情况进行特判,而对于其他情况只需要枚举\(\phi(p)\)的因子即可。

因为当9和p互质时,若有 n 对 $10^n \equiv 1\pmod {9p} $成立,那么一定有\(10^n \equiv 1\pmod {p}\) 成立

代码

计算\(\phi(9p)\)快速乘方法

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
int n,m,p;
int q[N],c[N],tot;
ll mul(ll a, ll b, ll p) {
    ll ret = 0;
    while (b) {
        if (b & 1) ret = (ret + a) % p;
        b /= 2;
        a = (a + a) % p;
    }
    return ret;
}
ll ksm(ll a,ll b,ll mod){
    ll res = 1;
    for(;b;b>>=1){
        if(b&1)res = mul(res,a,mod);
        a = mul(a,a,mod);
    }
    return res;
}
ll getphi(ll x){
    ll res = x;
    for(ll i=2;i*i <= x;i++){
        if(x % i == 0){
            res = res / i * (i-1);
            while(x % i == 0)x /= i;
        }
    }
    if(x > 1)res = res / x * (x - 1);
    return res;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&p,&n,&m);
        if(p == 2 || p == 5){
            puts("0");
            continue;
        }
        ll phi = getphi(9ll * p);
        ll fac = 1e18;
        for(ll i=2;i*i<=phi;i++){
            if(phi % i == 0){
                if(ksm(10,i,p * 9ll) == 1)fac = min(fac,i);
                if(ksm(10,phi/i,p * 9ll) == 1)fac = min(fac,phi/i);
            }
        }
        tot = 0;
        for(int i=2;i*i<=fac;i++){
            if(fac % i == 0){
                q[++tot] = i;c[tot] = 0;
                while(fac % i == 0)fac /= i,c[tot] ++;
            }  
        }
        if(fac > 1)q[++tot] = fac,c[tot] = 1;
        ll res = 0;
        int mx = 0;
        for(int i=1;i<=tot;i++) mx = max(mx,c[i]);
        for(int j=1;j <= mx && j <= m;j++){
            int now = 1;
            for(int i=1;i<=tot;i++){

                int k = c[i] / j + (c[i]%j != 0);

                for(int o = 1;o<=k;o++)now *= q[i];
            }
            res += n / now;
        }
        if(m > mx){
            int now = 1;
            for(int i=1;i<=tot;i++)now *= q[i];
            res += 1ll * (m-mx) * (n / now);
        }
        printf("%lld\n",res);
    }
    return 0;
}

标程用的方法

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<pair<int, int> > plist;
int pow_mod(int x, int k, int p)
{
	int ret = 1;
	for (; k; k>>=1)
	{
		if (k&1) ret = 1LL*ret*x%p;
		x = 1LL*x*x%p;
	}
	return ret;
}
int f(int n, int k)
{
	int d = 1;
	for (auto pv: plist)
	{
	 	int t = (pv.second+k-1) / k;
	 	while (t--) d *= pv.first;
    }
	return n/d;
}
int main()
{
    int T, n, m, p, d, D;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d %d %d", &p, &n, &m);
		if (p == 2 || p == 5) {puts("0"); continue;}
		if (p == 3) // 10^d = 1 mod 27 特判p = 3
		{
			//phi(27) = 18
			D = 18;
			p = 27;
		}
		else D = p-1;
		assert(pow_mod(10, D, p) == 1);
		d = 1e9;
		for (int i = 1; i*i <= D; ++i)
		{
			if (D % i) continue;
			if (pow_mod(10, i, p) == 1)
			   d = min(d, i);
            if (pow_mod(10, D/i, p) == 1)
               d = min(d, D/i);
		}
		for (int i = 2; i*i <= d; ++i)
		{
		 	if (d % i) continue;
			int c = 0;
			while (d % i == 0) ++c, d /= i;
			plist.push_back(make_pair(i, c));	
		}
		if (d != 1) plist.push_back(make_pair(d, 1));
		LL ans = 0;
		for (int i = 1; i <= 30 && i <= m; ++i)
			ans += f(n, i);
		if (m > 30) ans += 1LL*(m-30)*f(n, 30);
		printf("%lld\n", ans);
		plist.clear();
	}
	return 0;
}
posted @ 2019-07-25 23:05  kpole  阅读(274)  评论(0编辑  收藏  举报