【luogu P6860】象棋与马(数学)(杜教筛)
象棋与马
题目链接:luogu P6860
题目大意
给你一个数 n,然后问你有多少个 a<=n,b<=n 满足在一个二维网格上从源点出发每次走一个 a*b 的矩阵,能走到图上的所有整点。
思路
首先考虑怎样的点对是满足条件的。
首先我们考虑如果它能从 \((0,0)\) 走到 \((0,1)\),那它就能走到所有点。(因为你可以一步一步走)
那接着考虑怎么搞,首先顺序无关,所以我们可以把它分成两段位移:\((\pm x,\pm y)\) 和 \((\pm y,\pm x)\)。
然后第一段的整体位移可以表示成 \((2ax,2by)\) 或者 \((2ax+x,2by+y)\),第二段就是 \((2cy,2dx)\) 或 \((2cy+y,2dx+x)\)。
考虑列出四种可能的方法:
\(\left\{\begin{matrix} 2ax=2cy \\ 2by=2dx+1 \end{matrix}\right.\)
\(\left\{\begin{matrix} 2ax+x=2cy \\ 2by+y=2dx+1 \end{matrix}\right.\)
\(\left\{\begin{matrix} 2ax=2cy+y \\ 2by=2dx+x+1 \end{matrix}\right.\)
\(\left\{\begin{matrix} 2ax+x=2cy+y \\ 2by+y=2dx+x+1 \end{matrix}\right.\)
然后解四个方程,第一是 \(2(ax-cy)=0,2(by-dx)=1\),由于 \(x,y\) 互质,所以 \(by-dx\) 会可以是任意整数(设为 \(k\) 下同):
\(2k=0,2k=1\),无解
第二个是跟上面差不多,就是 \(2k+x=0,2k+y=1\),那就是 \(x\) 是偶数,\(y\) 是奇数。
第三个就是 \(2k-y=0,2k-x=1\),那就是 \(y\) 是偶数,\(x\) 是奇数。
第四个是 \(2k+x-y=0,2k+y-x=1\),那就是无解。
那综上,我们可以总结出合法的情况:
\(\gcd(i,j)=1\),且 \(i+j\) 是奇数。
然后考虑如何继续求,那 \(i+j\) 是奇数,\(i-j\) 自然也是。
然后根据辗转相减法:\(\gcd(i,j)=1\) 就有 \(\gcd(i,i-j)=1\)
你考虑设 \(i>j\)(到时算出来答案乘 \(2\),因为显然 \(i=j\) 是不成立)
\(\sum\limits_{i=1}^n\sum\limits_{j=1}^{i-1}2[(gcd(i,j)=1)\&((i-j)\bmod 2=1)]\)
然后发现如果不看奇偶它就是 \(\sum\limits_{i=1}^n\varphi(i)\),那你考虑有了右边之后会怎么样。
考虑这个新的值是 \(w(i)\)。
(原来的式子变成:\(\sum\limits_{i=1}^n2w(i)\))
考虑从 \(\varphi(i)\) 的性质考虑,如果 \(i\) 是偶数,那所有偶数跟它都不是互质的,那这个操作就不会影响,所以 \(w(i)=\varphi(i)\)。
如果 \(i\) 是奇数,那我们考虑一个东西,就是这个互质的对称性。
什么意思呢,就是如果 \(i,j\) 互质(\(i>j\)),那么 \(i,i-j\) 也互质。
那我们就其实可以看做是 \(w(i)=\dfrac{\varphi(i)}{2}\),当然你会发现 \(w(1)\) 要特判掉。(应该是 \(0\) 而不是 \(\dfrac{1}{2}\))
(其实可以不特判,然后到时答案减 \(1\) 即可,减一是因为后面乘 \(2\))
然后你就可以列出 \(w(i)=\left\{\begin{matrix}\varphi(i)&i\mod2=0 \\ \dfrac{\varphi(i)}{2}&i\mod2=1\end{matrix}\right.\)
然后把 \(2\) 放进去:
\(2w(i)=\left\{\begin{matrix}2\varphi(i)&i\mod2=0 \\ \varphi(i)&i\mod2=1\end{matrix}\right.\)
\(2w(i)=\varphi(i)+\left\{\begin{matrix}\varphi(i)&i\mod2=0 \\ 0&i\mod2=1\end{matrix}\right.\)
然后放回原来的式子:
\(\sum\limits_{i=1}^n2w(i)\)
\(\sum\limits_{i=1}^n\varphi(i)+\sum\limits_{i=1}^n\left\{\begin{matrix}\varphi(i)&i\mod2=0 \\ 0&i\mod2=1\end{matrix}\right.\)
那我们再看右边的部分,那只看有值的,就是偶数的。
然后再从递推式求的方式来看 \(\dfrac{i}{2}\),如果它是偶数那返回的就是 \(\varphi(\dfrac{i}{2})\),否则就是 \(\dfrac{\varphi(\frac{i}{2})}{2}\),你会发现它就是 \(w(\left\lfloor\dfrac{i}{2}\right\rfloor)\) 的值。
然后就有:
\(\sum\limits_{i=1}^nw(i)=\sum\limits_{i=1}^n\varphi(i)+\sum\limits_{i=1}^{\left\lfloor\frac{i}{2}\right\rfloor}w(i)\)
左边的用杜教筛,右边的递归下去就好了。
复杂度是 \(O(n^{\frac{2}{3}}\log n)\),看似很危,但其实跑不满(每次递归的 \(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;
}