Luogu 4388 付公主的矩形
还是挺妙的。
发现对于一个$r$行$c$列的矩阵,穿过的格子数$n = r + c - gcd(r, c)$,题目中其实给定了这个$n$,要我们计算满足这个式子的$r$和$c$的个数。
发现$n$一定要是$gcd(r, c)$的倍数,等式两边可以除掉这个$gcd(r, c)$,变成$n' = r' + c' - 1$。
那么这时候$gcd(r', c') = gcd(n' + 1 - r', c') = 1$。
根据辗转相减法,有$gcd(n' + 1, c') = 1$,而满足这个式子的$c'$的个数恰好是$\varphi (n' + 1)$。
于是可以开心地计算出$c'$的总数$sum = \sum_{d|n} \varphi (d + 1)$。
注意到这时$(n, n)$这一对数只算了一遍,所以最后的答案$ans = (sum + 1) / 2$。
线性筛一波就好啦,时间复杂度$O(n)$。
Code:
#include <cstdio> #include <cstring> using namespace std; const int N = 1e6 + 5; int n, pCnt = 0, ans = 0, pri[N], phi[N]; bool np[N]; inline void sieve() { phi[1] = 0; for(int i = 2; i <= n + 1; i++) { if(!np[i]) pri[++pCnt] = i, phi[i] = i - 1; for(int j = 1; j <= pCnt && i * pri[j] <= n + 1; j++) { np[i * pri[j]] = 1; if(i % pri[j] == 0) { phi[i * pri[j]] = phi[i] * pri[j]; break; } phi[i * pri[j]] = phi[i] * phi[pri[j]]; } } } int main() { scanf("%d", &n); sieve(); for(int i = 1; i <= n; i++) if(n % i == 0) ans += phi[i + 1]; printf("%d\n", (ans + 1) / 2); return 0; }