Loading

P6860 象棋与马 杜教筛

P6860 象棋与马 杜教筛

题意

有一个无限大的棋盘,有一个马最初在\((0,0)\),它的每步可以走一个\(a \times b\)的矩形,即能够走到\((x \pm a,y \pm b),或者(x \pm b,y \pm a)\)

若马通过上述移动方式可以达到棋盘中任意一点,那么\(p(a,b) = 1\),否则\(p(a,b) = 0\)

给出\(T\)组询问,每组询问会给出一个正整数\(n\),求出

\[(\sum_{a=1}^n\sum_{b=1}^np(a,b))mod2^{64} \]

\[n \times T \leq 10^{11}\\ T \leq 5 \]

分析

走到棋盘的充要条件是能够走到\((1,0)\)

如果不能走到\((1,0)\),显然无法走到棋盘的任一点,如果能走到\((1,0)\),显然可以走到棋盘的任意位置

由于只有本质不同的4种走法

  • \((a,b)\)
  • \((b,a)\)
  • \((-a,b)\)
  • \((-b,a)\)

走到\((1,0)\)的充要条件是

\[x_1 \cdot a + x_2 \cdot b - y_1 \cdot a - y_2 \cdot b = 1\\ x_1 \cdot b + x_2 \cdot a + y_1 \cdot b + y_2 \cdot a = 0 \]

上下相加,有

\[(x_1+ x_2 - (y_1 - y_2)) a + (x_1 + x_2 + (y_1 - y_2))b = 1 \]

\[(x - y)a + (x + y) b = 1 \]

可见

\[(a,b) = 1\\ \]

由于$(x + y) \(与\)(x - y)$必奇偶性同,且必全为奇数,若是偶数,显然右边不等于1

可以得出a,b奇偶性不同,若相同,右边不等于0

下面进行对答案讨论

\(a\)为偶数,只需要求出为奇数且与\(a\)互质的数,答案显然是\(\phi(a)\)

\(a\)为奇数,只需要求出为偶数且与\(a\)互质的数,答案为\(\frac{\phi(a)}{2}\),这是因为,\(a\)为奇数时,与它互质的数总是成对出现的

这是发现答案就是

\[ans = \sum\phi(i) + \sum_{i 为偶数}\phi(i) \]

由于\(n\)达到1e11,这就成为了杜教筛的模板题

至于后面那一坨,考虑用递归式处理

\[S(n) = \sum \phi(i)\\ ans(n) = S(n) + ans(n / 2) \]

用到的欧拉函数性质

\[a \% 2 == 1,\phi(2a) = \phi(a)\\ a \% 2== 0,\phi(2a) = 2\phi(a) \]

代码

map<ull, ull> Sphi;

const int M = 1e7 + 5;
ull prime[M], vis[M], phi[M], s[M], n, m;

void init() {
    phi[1] = 1;
    for (re int i = 2; i <= M - 5; i++) {
        if (!vis[i]) {
            vis[i] = i;
            prime[++m] = i;
            phi[i] = i - 1;
        }
        for (re int j = 1; j <= m && prime[j] *i <= M - 5; j++) {
            if (prime[j] > vis[i]) break;
            vis[i * prime[j]] = prime[j];
            phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
        }
    }
    for (re int i = 1; i <= M - 5; i++) {
        if (i & 1) s[i] = phi[i << 1] / 2;
        else s[i] = phi[i];
        s[i] += s[i - 1];
        phi[i] += phi[i - 1];
    }
}

ull getphi(ull n) {
    if (n <= M - 5)return phi[n];
    if (Sphi[n]) return Sphi[n];
    ull res = 0;
    if (n % 2 == 0) res = n / 2 * (n + 1);
    else res = (n + 1) / 2 * n;
    for (ull l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l);
        res -= (r - l + 1) * getphi(n / l);
    }
    return Sphi[n] = res;
}

ull S(ull n) {
    if (n <= 1) return 0;
    return getphi(n) + S(n / 2);
}

int main() {
    init();
    int T = readint();
    while (T--) {
        n = readull();
        printf("%llu\n", S(n));
    }
}
posted @ 2020-10-28 22:22  MQFLLY  阅读(135)  评论(0编辑  收藏  举报