【nowcoder 225876】与巨(数论)

与巨

题目链接:nowcoder 225876

题目大意

问你有多少个 1~n 的数满足它乘上一个 c 之后它原来不是前导 0 的位跟之前一样。

思路

首先它只要留下前面的一些位,那我们可以相当于它是一个取模操作。

(如果是 t 位,那就是 mod(2t+1)

然后我们进行一个推式子:
icmod(2t+1)=i
i(c1)=0mod(2t+1)

那我们可以找到最低位的 1,表示成 2px

然后 i 就一定要有 2t+1p 这个因子。

然后设 g=t+1p,然后我们考虑枚举 t,进行一个类似数位 DP 的感觉。
如果 t<m1n 最大 m 位),那你就是随便选的。
那每次会满足的就分别是 2t,2t+2g,2t+22g,...,2t+x2g(2t+(x+1)2g=2t+1)
然后 x=2t+12g,每次多了 2g,这个是一个等差序列,可以很快求和。
(然后别忘了每次会满足的会贡献 2g 次,所以要乘上 2g

然后考虑 t=m1,那我们考虑能选多少。
我们要满足:2t+x2gn
然后通过移项等可以得到:x=n2g2tg

然后也是一个等差序列计算,但是你会发现最后移项不一定是计算 2g 次。

它的计数范围是 [2t+x2g,2t+(x+1)2g1],然后实际要的是 n,多了 2t+(x+1)2g1n 次。
然后就减去这些的贡献,记得要乘上 2t+x2g,就是 (2t+(x+1)2g1n)(2t+x2g)

然后就好啦。

代码

#include<cstdio> #include<cstring> #include<iostream> #define ll long long #define mo 998244353 using namespace std; int T, m, c, p; char n[10000001]; ll ans, two[10000001], inv2; ll ksm(ll x, ll y) { ll re = 1; while (y) { if (y & 1) re = re * x % mo; x = x * x % mo; y >>= 1; } return re; } ll inv(ll x) { return ksm(x, mo - 2); } ll clac(ll s, ll t, ll n) {//三个分别是首项,尾项,个数 return (s + t) % mo * n % mo * inv2 % mo; } int main() { two[0] = 1; for (int i = 1; i <= 10000000; i++) two[i] = two[i - 1] * 2 % mo; inv2 = inv(2);//预处理出 2 的逆元,每次都要 log 会超时 scanf("%d", &T); while (T--) { scanf("%s %d", n + 1, &c); m = strlen(n + 1); c--; if (!c) { ll re = 0; for (int i = 1; i <= m; i++) { re = re * 2 + n[i] - '0'; re %= mo; } printf("%lld\n", (re + 1) * re % mo * inv2 % mo); continue; } if (c & 1) { printf("0\n"); continue; } p = 0; while (!(c & 1)) { p++; c >>= 1; } ans = 0; for (int t = 0; t < m - 1; t++) { int g = max(0, t + 1 - p); ans = (ans + two[g] * clac(two[t], (two[t + 1] - two[g] + mo) % mo, two[t - g])) % mo; ans %= mo; } int t = m - 1; ll N = 0; int g = max(0, (m - 1 + 1) - p); for (int i = 1; i <= m - g; i++) N = ((N << 1) + n[i] - '0') % mo; N = (N - two[t - g] + 1 + mo) % mo; ans = ans + two[g] * clac(two[t], (two[t] + (N - 1 + mo) % mo * two[g] % mo) % mo, N) % mo; ans %= mo; ll lst = (two[t] + (N - 1 + mo) % mo * two[g] % mo) % mo; ll l = 0; for (int i = 1; i <= m; i++) l = ((l << 1) + n[i] - '0') % mo; ll r = (lst + two[g] - 1 + mo) % mo; ans -= (r - l + mo) % mo * lst % mo; ans = (ans % mo + mo) % mo; printf("%lld\n", ans); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/nowcoder_225876.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(57)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示