P6860 象棋与马 杜教筛

P6860 象棋与马 杜教筛

题意

有一个无限大的棋盘,有一个马最初在((0,0)),它的每步可以走一个(a imes 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}^nsum_{b=1}^np(a,b))mod2^{64} ]

[n imes 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 = sumphi(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) = 2phi(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
", S(n));
    }
}
原文地址:https://www.cnblogs.com/hznumqf/p/13893925.html