牛客网暑期ACM多校训练营(第三场) D.Big Intege良心题解

D.Big Intege

题意

已知 A ( n ) = 11111 ⋯ 11111 A(n)=11111\cdots11111 A(n)=1111111111(n为1的数量)
A ( i j ) m o d    p = 0 A(i^j) \mod p=0 A(ij)modp=0的数量 ( 1 ≤ i ≤ n , 1 ≤ j ≤ m ) (1\leq i \leq n,1\leq j \leq m) (1in,1jm)

思路

公式推导

第一步

n = i j n=i^j n=ij
显然, 11111 ⋯ 11111 11111\cdots11111 1111111111是个很优美的数字
A ( n ) = 1 0 n − 1 9 A(n)=\frac{10^n-1}{9} A(n)=910n1

第二步

1 0 n − 1 9 % p = = 0 \frac{10^n-1}{9} \% p==0 910n1%p==0
除了 p = = 3 p==3 p==3, p p p 9 9 9都是互质的,那么

  • 1 0 n − 1 9 % p \frac{10^n-1}{9} \% p 910n1%p
  • = ( 1 0 n − 1 ) ∗ i n v 9 % p =(10^n-1)*inv9\% p =(10n1)inv9%p
  • = ( ( 1 0 n − 1 ) % p ) ∗ ( i n v 9 % p ) % p =((10^n-1)\% p)*(inv9 \% p) \%p =((10n1)%p)(inv9%p)%p
  • = 0 =0 =0
第三步

因为互质
i n v 9 ≠ 0 inv9 \neq 0 inv9̸=0
( ( 1 0 n − 1 ) % p ) ∗ ( i n v 9 % p ) % p ((10^n-1)\% p)*(inv9 \% p) \%p ((10n1)%p)(inv9%p)%p
所以推出 1 0 n − 1 % p = = 0 10^n-1 \% p==0 10n1%p==0 1 0 n ≡ 1 m o d    p 10^n\equiv 1\mod p 10n1modp

第四步

用以下随便哪个定理都一样
费马小定理:
**若 a a a不是 p p p的倍数, a p − 1 ≡ 1 m o d    n a^{p-1} \equiv 1 \mod n ap11modn **
显然 p − 1 p-1 p1属于 n n n,但 p − 1 p-1 p1不一定最小的满足的数
若存在 d ( d ≤ p − 1 ) d(d \leq p-1) d(dp1)满足上式, d d d一定是 p − 1 p-1 p1的约数(同余式的性质)

欧拉定理引理:
a , n a,n a,n互质,则满足 a x ≡ 1 m o d    n a^x\equiv 1 \mod n ax1modn的最小正整数 x x x Θ ( n ) \Theta(n) Θ(n)的约数

如此枚举约数即可求出最小的x满足 1 0 x ≡ m o d    1 10^x \equiv \mod 1 10xmod1

int big_d[32000];
LL Get_Min_d(LL p) {
	LL phi = p - 1; int k = 0;
	for (int i = 1; i * i <= phi; i++) {
		if (phi % i == 0) {
			if (quickpow(10, i, p) == 1)	//快速幂
				return i;
			big_d[++k] = phi / i;	//大的因数先存起来
		}
	}
	for (int i = k; i >= 1; i--) {		//小的因数找不到,再找大的
		if (quickpow(10, big_d[i], p) == 1)
			return big_d[i];
	}
}

第五步

显然满足 1 0 d m o d &ThinSpace;&ThinSpace; p = = 1 10^d \mod p==1 10dmodp==1的最小循环节d只有一个
证明:
若存在 d 1 , d 2 d_{1},d_{2} d1,d2 d 1 d_{1} d1 d 2 d_{2} d2互质
1 0 d 1 m o d &ThinSpace;&ThinSpace; p = = 1 10^{d_{1}} \mod p==1 10d1modp==1
1 0 d 2 m o d &ThinSpace;&ThinSpace; p = = 1 10^{d_{2} }\mod p==1 10d2modp==1
那么必定存在 1 0 ( d 2 − d 1 ) m o d &ThinSpace;&ThinSpace; p = = 1 10^{(d_{2}-d_{1})} \mod p==1 10(d2d1)modp==1,依次类推做辗转相减法
存在 d = g c d ( d 1 , d 2 ) d=gcd(d_{1},d_{2}) d=gcd(d1,d2),那么d就是唯一循环节

问题转化

若我们找到最小循环节d,那么所有满足 1 0 d m o d &ThinSpace;&ThinSpace; p = = 1 10^d \mod p==1 10dmodp==1,那么只要找到所有 i j i^j ij中的倍数即可

i j i^j ij中d的倍数

ps:此处公式中 [ x ] [x] [x]为将x向上取整

第一步

将d分解质因数, d = p 1 a 1 ∗ p 2 a 2 ∗ p 3 a 3 ∗ ⋯ ∗ p n a n d=p_{1}^{a_{1}}*p_{2}^{a_{2}}*p_{3}^{a_{3}}*\cdots*p_{n}^{a_{n}} d=p1a1p2a2p3a3pnan

第二步

若确定 j j j,则 i i i一定是min_d的倍数
m i n ( d ) = p 1 [ a 1 j ] ∗ p 2 [ a 2 j ] ∗ p 3 [ a 3 j ] ∗ ⋯ ∗ p n [ a n j ] min(d)=p_{1}^{[\frac{a_{1}}{j}]}*p_{2}^{[\frac{a_{2}}{j}]}*p_{3}^{[\frac{a_{3}}{j}]}*\cdots*p_{n}^{[\frac{a_{n}}{j}]} min(d)=p1[ja1]p2[ja2]p3[ja3]pn[jan]

因为
m i n ( d ) j = p 1 [ a 1 j ] ∗ j ∗ p 2 [ a 2 j ] ∗ j ∗ p 3 [ a 3 j ] ∗ j ∗ ⋯ ∗ p n [ a n j ] ∗ j min(d)^j=p_{1}^{[\frac{a_{1}}{j}]*j}*p_{2}^{[\frac{a_{2}}{j}]*j}*p_{3}^{[\frac{a_{3}}{j}]*j}*\cdots*p_{n}^{[\frac{a_{n}}{j}]*j} min(d)j=p1[ja1]jp2[ja2]jp3[ja3]jpn[jan]j
= d ∗ p 1 [ a 1 j ] ∗ j − a 1 ∗ p 2 [ a 2 j ] ∗ j − a 2 ∗ p 3 [ a 3 j ] ∗ j − a 3 ∗ ⋯ ∗ p n [ a n j ] ∗ j − a n =d*p_{1}^{[\frac{a_{1}}{j}]*j-a_{1}}*p_{2}^{[\frac{a_{2}}{j}]*j-a_{2}}*p_{3}^{[\frac{a_{3}}{j}]*j-a_{3}}*\cdots*p_{n}^{[\frac{a_{n}}{j}]*j-a_{n}} =dp1[ja1]ja1p2[ja2]ja2p3[ja3]ja3pn[jan]jan

例如:对于 12 = 2 2 ∗ 3 1 12=2^2*3^1 12=2231
j = 1 j=1 j=1, m i n ( d ) = 2 2 ∗ 3 1 = 12 min(d)=2^2*3^1=12 min(d)=2231=12, 12 12 12的倍数的 1 1 1次方,为 12 12 12的倍数
j = 2 j=2 j=2, m i n ( d ) = 2 1 ∗ 3 1 = 6 min(d)=2^1*3^1=6 min(d)=2131=6, 6 6 6的倍数的 2 2 2次方,为 12 12 12的倍数
j = 3 j=3 j=3, m i n ( d ) = 2 1 ∗ 3 1 = 6 min(d)=2^1*3^1=6 min(d)=2131=6, 6 6 6的倍数的 3 3 3次方,为 12 12 12的倍数

第三步

显然 1 − n 1-n 1n中min_d的倍数个数为 n / m i n ( d ) n/min(d) n/min(d)
对于 1 ≤ j ≤ m a x ( a 1 , a 2 , ⋯ &ThinSpace; , a n ) 1\leq j\leq max(a_{1},a_{2},\cdots,a_{n}) 1jmax(a1,a2,,an), m i n ( d ) min(d) min(d)是不同的
对于 m a x ( a 1 , a 2 , ⋯ &ThinSpace; , a n ) &lt; j max(a_{1},a_{2},\cdots,a_{n})&lt; j max(a1,a2,,an)<j, m i n ( d ) = p 1 ∗ p 2 ∗ p 3 ∗ ⋯ ∗ p n min(d)=p_{1}*p_{2}*p_{3}*\cdots*p_{n} min(d)=p1p2p3pn
先分解质因数
再枚举 j j j计算,min_d的倍数个数

分解质因数
int p[32], a[32], cnt;	//p数组为质因数,a数组为次数
void Dec(LL d) {
	fill(p, p + 32, 0);
	fill(a, a + 32, 0); cnt = 0;
	for (int i = 2; i * i <= d; i++) {
		if (d % i == 0) {
			p[++cnt] = i;
			while (d % i == 0) {
				d /= i;
				a[cnt]++;
			}
			if (d == 1)return;
		}
	}
	if (d > 1) p[++cnt] = d, a[cnt] = 1;
	return;
}
枚举j,计算d的倍数个数
LL p_power[32][32];
LL Cal(LL n, LL m) {
	for (int i = 1; i <= cnt; i++) {		//预处理,所有p[i]^a[i]
		p_power[i][1] = p[i];
		for (int j = 2; j <= a[i]; j++)
			p_power[i][j] = p_power[i][j - 1] * p[i];
	}

	int limit = 0;			//1-max(a[1]----a[n])的min_d都要单独计算
	for (int i = 1; i <= cnt; i++)limit = max(limit, a[i]);
	limit = min(LL(limit), m);

	int min_d = 1; LL ans = 0;
	for (int i = 1; i <= limit; i++) {		//枚举j计算
		min_d = 1;
		for (int j = 1; j <= cnt; j++)
			min_d *= p_power[j][ceil(a[j], i)];
		ans += n / min_d;
	}
	if (m > limit)ans += n / min_d * (m - limit);
	return ans;
}

AC代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
#define ceil(a,b) a % b == 0 ? a/b : a/b+1
LL quickpow(LL m, LL p, LL mod) {
	LL res = 1;
	while (p) {
		if (p & 1)
			res = res * m % mod;
		m = m * m % mod;
		p >>= 1;
	}
	return res;
}
int big_d[32000];
LL Get_Min_d(LL p) {
	LL phi = p - 1; int k = 0;
	for (int i = 1; i * i <= phi; i++) {
		if (phi % i == 0) {
			if (quickpow(10, i, p) == 1)
				return i;
			big_d[++k] = phi / i;
		}
	}
	for (int i = k; i >= 1; i--) {
		if (quickpow(10, big_d[i], p) == 1)
			return big_d[i];
	}
}
int p[32], a[32], cnt;
void Dec(LL d) {
	fill(p, p + 32, 0);
	fill(a, a + 32, 0); cnt = 0;
	for (int i = 2; i * i <= d; i++) {
		if (d % i == 0) {
			p[++cnt] = i;
			while (d % i == 0) {
				d /= i;
				a[cnt]++;
			}
			if (d == 1)return;
		}
	}
	if (d > 1) p[++cnt] = d, a[cnt] = 1;
	return;
}
LL p_power[32][32];
LL Cal(LL n, LL m) {
	for (int i = 1; i <= cnt; i++) {
		p_power[i][1] = p[i];
		for (int j = 2; j <= a[i]; j++)
			p_power[i][j] = p_power[i][j - 1] * p[i];
	}

	int limit = 0;
	for (int i = 1; i <= cnt; i++)limit = max(limit, a[i]);
	limit = min(LL(limit), m);

	int min_d = 1; LL ans = 0;
	for (int i = 1; i <= limit; i++) {
		min_d = 1;
		for (int j = 1; j <= cnt; j++)
			min_d *= p_power[j][ceil(a[j], i)];
		ans += n / min_d;
	}
	if (m > limit)ans += n / min_d * (m - limit);
	return ans;
}
int main() {
	int  t; scanf("%d", &t);
	LL p, n, m;
	while (t--) {
		scanf("%lld%lld%lld", &p, &n, &m);
		if (p == 2 || p == 5) {
			printf("0\n");
			continue;
		}
		else if (p == 3){
			printf("%lld\n", n / 3 * m);
			continue;
		}
		LL d = Get_Min_d(p);
		Dec(d);
		LL ans = Cal(n, m);
		printf("%lld\n", ans);
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值