[Luogu P4884] 多少个1?

3 篇文章 0 订阅
洛谷传送门

题目描述

给定整数 K K K和质数 m m m,求最小的正整数 N N N,使得 1 ⋯ 1 1\cdots1 11 N N N 1 1 1 ≡ K ( m o d m ) \equiv K \pmod m K(modm)

说人话:就是 111...1111   m o d   m = K 111...1111\ mod\ m =K 111...1111 mod m=K

输入输出格式

输入格式:

第一行两个整数,分别表示 K K K m m m

输出格式:

一个整数,表示符合条件最小的 N N N

输入输出样例

输入样例#1:
9 17
输出样例#1:
3

说明

30%的数据保证 m ≤ 1 0 6 m\leq 10^6 m106

60%的数据保证 m ≤ 5 ∗ 1 0 7 m\leq 5*10^7 m5107

100%的数据保证 2 ≤ m ≤ 1 0 11 , 0 ≤ K &lt; m 2\leq m\leq 10^{11},0\leq K&lt; m 2m1011,0K<m

解题分析

11 ⋯ 11 11\cdots 11 1111不好表示, 我们就把它 × 9 \times 9 ×9 + 1 +1 +1, 然后就变成了 1 0 n ≡ K × 9 + 1 ( m o d   m ) 10^n\equiv K\times 9+1 (mod\ m) 10nK×9+1(mod m)

显然就可以用 B S G S BSGS BSGS搞了, 注意需要龟速乘。

总复杂度 O ( N l o g ( N ) ) O(\sqrt Nlog(\sqrt N)) O(N log(N ))

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <map>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
std::map <ll, int> mp;
IN ll fmul(ll a, ll b, ll mod)
{
	ll ret = 0;
	W (b)
	{
		if(b & 1) ret = (ret + a) % mod;
		a = (a << 1) % mod, b >>= 1;
	}
	return ret;
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
	if(!b) return x = 1, y = 0, a;
	ll ret = exgcd(b, a % b, x, y);
	ll buf = x; x = y, y = buf - a / b * y;
}
IN ll BSGS(ll A, ll B, ll mod)
{
	if(mod == 1) if(!B) return 0; else return -1;
	if(B == 1) if(A) return 0; else return -1;
	if(!(A % mod)) if(B % mod) return -1; else return 1;
	ll bd = std::ceil(std::sqrt(mod));
	ll now = 1, base = 1;
	for (R int i = 0; i < bd; ++i)
	{
		if(!mp.count(base)) mp[base] = i;
		base = fmul(base, A, mod);
	}
	ll x, y, gcd;
	for (R int i = 0; i < bd; ++i)
	{
		gcd = exgcd(now, mod, x, y);
		x = (x % mod + mod) % mod;
		x = fmul(x, B, mod);
		if(mp.count(x)) return mp[x] + 1ll * i * bd;
		now = fmul(now, base, mod);
	}
	return -1;
}
int main(void)
{
	ll b, mod;
	in(b), in(mod);
	b = (b * 9 + 1) % mod;
	printf("%lld", BSGS(10, b, mod));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值