[Cqoi 2017] bzoj4815 小Q的表格 [莫比乌斯反演]

Description:
自己看题。
Solution:
这个 f f 的形式和gcd很像,事实上每次修改 (a,b) ( a , b ) 会影响到 gcd(i,j)==gcd(a,b) g c d ( i , j ) == g c d ( a , b )
所以我们只用保留 gcd(i,i) g c d ( i , i ) 即可。每次修改 (a,b) ( a , b ) 等价于修改 gcd(a,b) g c d ( a , b )
然后维护 S(d) S ( d ) ,表示 f(d,d) f ( d , d ) 的前缀和。
那么得出答案即是
ki=1f(i)S([ki]) ∑ i = 1 k f ( i ) S ( [ k i ] )
S(n)=ni=1ϕ(i)i2 S ( n ) = ∑ i = 1 n ϕ ( i ) ∗ i 2

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 4e6 + 5, P = 1e9 + 7;
int n, m;
int mark[maxn], p[maxn], l[maxn], r[maxn], b[maxn];
ll phi[maxn], s[maxn], S[maxn], add[maxn], a[maxn];
int gcd(int a, int b) {
    return !b ? a : gcd(b, a % b);
}
ll power(ll x, ll t) {
    ll ret = 1;
    for(; t; t >>= 1, x = x * x % P) {
        if(t & 1) {
            ret = ret * x % P;
        }
    }
    return ret;
}
void ini() {
    phi[1] = 1;
    for(int i = 2; i < maxn; ++i) {
        if(!mark[i]) {
            p[++p[0]] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= p[0] && i * p[j] < maxn; ++j) {
            mark[i * p[j]] = 1;
            if(i % p[j] == 0) {
                phi[i * p[j]] = phi[i] * p[j];
                break;
            }
            phi[i * p[j]] = phi[i] * phi[p[j]];
        }
    }
    for(int i = 1; i < maxn; ++i) {
        s[i] = (s[i - 1] + phi[i] * i % P * i % P) % P;
    }
}
int main() {
    ini();
    scanf("%d%d", &m, &n);
    int B = sqrt(n);
    for(int i = 1; i <= n; ++i) {
        S[i] = (S[i - 1] + (long long) i * i % P) % P;
        a[i] = (long long) i * i % P;
        b[i] = (i - 1) / B + 1;
        if(!l[b[i]]) {
            l[b[i]] = i;
        }
        r[b[i]] = i;
    }
    while(m--) {
        int A, B, k;
        ll x, ans = 0;
        scanf("%d%d%lld%d", &A, &B, &x, &k);
        int d = gcd(A, B);
        x %= P;
        x = x * d % P * d % P * power((long long) A * B % P, P - 2) % P;
        for(int i = d; i <= r[b[d]]; ++i) {
            S[i] = (S[i] + x - a[d] + P) % P;
        }
        for(int i = b[d] + 1; i <= b[n]; ++i) {
            add[i] = (add[i] + x - a[d] + P) % P;
        }
        a[d] = x;
        for(int i = 1, j; i <= k; i = j + 1) {
            j = k / (k / i);
            ans = (ans + (S[j] + add[b[j]] - S[i - 1] - add[b[i - 1]] + 2 * P) % P * s[k / i] % P) % P;
        }
        printf("%lld\n", ans);
    }       
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值