JZOJ 4583【NOI2016模拟7.5】求和

JZOJ 4583【NOI2016模拟7.5】求和_第1张图片
1<=n,m<=500000

题解:

为什么这么多题目的名字叫求和?

这题其实有很多种做法,但是用一个非常简单的性质就可以做出来。。。
s ( p q ) = ∑ i ∣ p ∑ j ∣ q [ ( i , j ) = 1 ] ∗ i ∗ q / j s(pq)=\sum_{i|p}\sum_{j|q}[(i,j)=1]*i*{q/j} s(pq)=ipjq[(i,j)=1]iq/j
证明之前写过:
https://blog.csdn.net/Cold_Chair/article/details/78186552

那么就开始反演:
F ( n , m ) ( n < m ) F(n,m)(n<m) F(n,m)(n<m)
= ∑ i = 1 n ∑ j = 1 m μ ( i ) ∗ μ ( j ) ∗ ∑ x ∣ i ∑ y ∣ j [ ( x , y ) = 1 ] x j / y =\sum_{i=1}^n \sum_{j=1}^m\mu(i)*\mu(j)*\sum_{x|i}\sum_{y|j}[(x,y)=1]xj/y =i=1nj=1mμ(i)μ(j)xiyj[(x,y)=1]xj/y
中间过程略。
= ∑ d = 1 n μ ( d ) ∗ d ( ∑ i = 1 n / d ∑ j = 1 m / d μ ( d ∗ i ∗ x ) ∗ i ) ( ∑ x = 1 n / d / i ∑ y = 1 m / d / j μ ( d ∗ j ∗ y ) ∗ y ) =\sum_{d=1}^n\mu(d)*d(\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}\mu(d*i*x)*i)(\sum_{x=1}^{n/d/i}\sum_{y=1}^{m/d/j}\mu(d*j*y)*y) =d=1nμ(d)d(i=1n/dj=1m/dμ(dix)i)(x=1n/d/iy=1m/d/jμ(djy)y)

这个玩意做几次倍数统计就行了。

Code:

#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

int n, m;

const int N = 5e5 + 5;
int bz[N], p[N], mu[N];

void sieve(int n) {
    fo(i, 2, n) {
        if(!bz[i]) p[++ p[0]] = i, mu[i] = -1;
        for(int j = 1; i * p[j] <= n; j ++) {
            int k = i * p[j];
            bz[k] = 1;
            if(i % p[j] == 0) {
                mu[k] = 0; break;
            }
            mu[k] = -mu[i];
        }
    }
    mu[1] = 1;
}

const int mo = 998244353;

ll a[N], b[N];

int main() {
    scanf("%d %d", &n, &m);
    if(n > m) swap(n, m);
    sieve(m);
    fo(i, 1, n) for(int j = i; j <= n; j += i) a[i] += mu[j];
    fo(i, 1, m) for(int j = i, k = 1; j <= m; j += i, k ++)
        b[i] += k * mu[j], b[i] %= mo;
    ll ans = 0;
    fo(i, 1, n) {
        ll s1 = 0, s2 = 0;;
        for(int j = i, k = 1; j <= n; j += i, k ++)
            s1 = (s1 + (ll) k * a[j]) % mo;
        for(int j = i; j <= m; j += i) s2 = (s2 + b[j]) % mo;
        ans += s1 * s2 % mo * mu[i] * i % mo;
   }
   printf("%lld", (ans % mo + mo) % mo);
}

你可能感兴趣的:(莫比乌斯反演)