P4884 多少个1?

签到题爆零系列。。。

开局先说些有的没的。这道题是9月月赛的T1,题目名字叫做“签到题”,结果难度给了省选。。。真的毒瘤!


题面就是让你求\(11\cdots1111(N个1) \equiv K \pmod m\)。其中他说\(m\)是质数。

暴力模拟一分都拿不到!很气!

下面我们来看正解。。。

其实这\(N个1\)是可以有通项公式的。数学必修五学到这个公式。\(a_n=\frac{10^n-1}{9}\)

所以那个同余的式子就能变成\(\frac{10^n-1}{9} \equiv K \pmod m\)

同余的等号也有类似于一般的等号。我们可以左右各乘以9再加1。结果就变成了\(10^n \equiv 9K+1 \pmod m\)。这种求指数的同余方程叫做什么离散对数。

接下来介绍一个名字叫做大步小步算法(baby-step giant-step algorithm)。这个算法就是专门来解决这类问题的。

我们考虑一个一般性的方程\(y^x \equiv z \pmod p\)

温馨提示:下面的东西有点玄学。。。

我们设\(m=\lceil \sqrt p \rceil\),令我们要求的\(x=a \times m-b\)。这里很巧妙设为减,后面有大作用。

所以变成了\(y^{a \times m - b} \equiv z \pmod p\),移项得\(y^{a \times m} \equiv z \times y^b \pmod p\)

这里注意下取值范围:上面我们定义了两个新的变量\(a和b\)\(a \in (0,m+1],b \in [0,m)\)

我们先算出所有的\(z \times y^b\)的值,然后用一个hash表记录下来,我们就能用值来取出指数\(b\)。这里应该是baby-step部分。

处理完右边,我们可以处理左边了。这里把\(y^{a\times m}\)变成\({y^m}^a\)。这里是giant-step部分:弄出一个变量giant\(y^m\)。然后我们套\(a\)次的循环,每一次循环算出左边的模值,看看hash表里面有没有,如果有的话你就找到解了,解的值为\(i \times m - b\)返回掉。

但是这个方程也可能是无解的。我们拿-1来作为无解的标志就行了。

差不多就是这样了。看起来算法思想很玄学,但是实现起来还可以。

对了,这个算法有前置技能:快速乘和快速幂。一个使用乘法分配律,用加的,另一个用指数的性质,用乘的。

注意:快速幂的乘可以用快速乘来优化。所谓的快速乘就是来防止你乘完中间过程还是什么的爆long long。

快速乘有\(O(logn)\)的,也有\(O(1)\)的。这里卡log的快速乘。。。真的毒瘤!

代码:

#include<cstdio>
#include<cmath>
#include<map>

#define ll long long
ll k, mod;

ll mul_mod(ll a, ll b)
{
    ll ans = 0, base = a;
    while(b)
    {
        if(b & 1) ans = (ans + base) % mod;
        base = (base + base) % mod;
        b >>= 1;
    }
    return ans % mod;
}
ll mult(ll a,ll b){
      auto ans=a*b-(ll)((long double)a/mod*b+1e-10)*mod;
      return (ans+mod)%mod;
}
ll pow_mod(ll a, ll b)
{
    ll ans = 1, base = a;
    while(b)
    {
        if(b & 1) ans = mult(ans, base);
        base = mult(base, base);
        b >>= 1;
    }
    return ans % mod;
}
ll bsgs(ll a, ll b)
{
    // a^x === b (mod mod)
    // x = a * m - b
    // a^(a * m - b) === b (mod mod)
    // a^(a * m) === b * a^b (mod mod)
    if(a == 0)
    {
        if(b == 0) return 1;
        else return -1;
    }
    std::map<ll, ll> hash;
    b = b % mod;
    ll m = sqrt(mod) + 1;
    for(int i = 0; i < m; i++)
    {
        ll val = mult(b, pow_mod(a, i));
        if(!hash.count(val)) hash[val] = i;
    }
    ll giant = pow_mod(a, m);
    for(int i = 0; i <= m; i++)
    {
        ll val = pow_mod(giant, i);
        ll j = hash.find(val) == hash.end() ? -1 : hash[val];
        if(j >= 0 && i * m - j >= 0) return i * m - j;
    }
    return -1;
}
int main()
{
    scanf("%lld%lld", &k, &mod);
    ll ans = bsgs(10, 9 * k + 1);
    printf("%lld\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/Garen-Wang/p/9694455.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值