【luogu P6860】象棋与马(数学)(杜教筛)

象棋与马

题目链接:luogu P6860

题目大意

给你一个数 n,然后问你有多少个 a<=n,b<=n 满足在一个二维网格上从源点出发每次走一个 a*b 的矩阵,能走到图上的所有整点。

思路

首先考虑怎样的点对是满足条件的。

首先我们考虑如果它能从 (0,0) 走到 (0,1),那它就能走到所有点。(因为你可以一步一步走)

那接着考虑怎么搞,首先顺序无关,所以我们可以把它分成两段位移:(±x,±y)(±y,±x)

然后第一段的整体位移可以表示成 (2ax,2by) 或者 (2ax+x,2by+y),第二段就是 (2cy,2dx)(2cy+y,2dx+x)

考虑列出四种可能的方法:
{2ax=2cy2by=2dx+1

{2ax+x=2cy2by+y=2dx+1

{2ax=2cy+y2by=2dx+x+1

{2ax+x=2cy+y2by+y=2dx+x+1

然后解四个方程,第一是 2(axcy)=0,2(bydx)=1,由于 x,y 互质,所以 bydx 会可以是任意整数(设为 k 下同):
2k=0,2k=1,无解

第二个是跟上面差不多,就是 2k+x=0,2k+y=1,那就是 x 是偶数,y 是奇数。

第三个就是 2ky=0,2kx=1,那就是 y 是偶数,x 是奇数。

第四个是 2k+xy=0,2k+yx=1,那就是无解。

那综上,我们可以总结出合法的情况:
gcd(i,j)=1,且 i+j 是奇数。


然后考虑如何继续求,那 i+j 是奇数,ij 自然也是。
然后根据辗转相减法:gcd(i,j)=1 就有 gcd(i,ij)=1
你考虑设 i>j(到时算出来答案乘 2,因为显然 i=j 是不成立)

i=1nj=1i12[(gcd(i,j)=1)&((ij)mod2=1)]

然后发现如果不看奇偶它就是 i=1nφ(i),那你考虑有了右边之后会怎么样。
考虑这个新的值是 w(i)
(原来的式子变成:i=1n2w(i)

考虑从 φ(i) 的性质考虑,如果 i 是偶数,那所有偶数跟它都不是互质的,那这个操作就不会影响,所以 w(i)=φ(i)
如果 i 是奇数,那我们考虑一个东西,就是这个互质的对称性。
什么意思呢,就是如果 i,j 互质(i>j),那么 i,ij 也互质。

那我们就其实可以看做是 w(i)=φ(i)2,当然你会发现 w(1) 要特判掉。(应该是 0 而不是 12
(其实可以不特判,然后到时答案减 1 即可,减一是因为后面乘 2

然后你就可以列出 w(i)={φ(i)imod2=0φ(i)2imod2=1

然后把 2 放进去:
2w(i)={2φ(i)imod2=0φ(i)imod2=1
2w(i)=φ(i)+{φ(i)imod2=00imod2=1

然后放回原来的式子:
i=1n2w(i)
i=1nφ(i)+i=1n{φ(i)imod2=00imod2=1

那我们再看右边的部分,那只看有值的,就是偶数的。
然后再从递推式求的方式来看 i2,如果它是偶数那返回的就是 φ(i2),否则就是 φ(i2)2,你会发现它就是 w(i2) 的值。

然后就有:
i=1nw(i)=i=1nφ(i)+i=1i2w(i)
左边的用杜教筛,右边的递归下去就好了。

复杂度是 O(n23logn),看似很危,但其实跑不满(每次递归的 n 越来越小)可以过。

代码

#include<map> #include<cstdio> #define ll unsigned long long using namespace std; //const ll Maxn = 21544346.900318837217592935665195; const ll Maxn = 22000000; int T; ll n, ans, phi[Maxn + 1], prime[Maxn / 10]; bool np[Maxn + 1]; map <int, ll> ans_phi; void init() {//phi 的预处理 phi[1] = 1; for (int i = 2; i <= Maxn; i++) { if (!np[i]) { phi[i] = i - 1; prime[++prime[0]] = i; } for (int j = 1; j <= prime[0] && i * prime[j] <= Maxn; j++) { if (i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j] - 1), np[i * prime[j]] = 1; else { phi[i * prime[j]] = phi[i] * prime[j], np[i * prime[j]] = 1; break; } } } for (int i = 1; i <= Maxn; i++) phi[i] += phi[i - 1]; } ll get_phi(ll n) {//杜教筛 if (n < Maxn) return phi[n]; if (ans_phi[n]) return ans_phi[n]; ll re = (n & 1llu) ? (1llu + n) / 2llu * n : n / 2llu * (1llu + n); for (ll l = 2, r; l <= n; l = r + 1) { r = n / (n / l); re -= (r - l + 1) * get_phi(n / l); } return ans_phi[n] = re; } ll F(ll n) { if (!n) return 0; return get_phi(n) + F(n / 2); } int main() { init(); scanf("%d", &T); while (T--) { scanf("%llu", &n); printf("%llu\n", F(n) - 1llu); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P6860.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(83)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示