51nod 1237 最大公约数之和 V3

16 篇文章 0 订阅
16 篇文章 0 订阅

题目大意:

G=0;
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
{
    G = (G + gcd(i,j)) % 1000000007;
}

题解:

杜教筛。
这里提供两种反演姿势,之后我们会发现一个神奇的东西。
在此之前你得会杜教筛φ。

反演1:

ni=1nj=1gcd(i,j)
=2ni=1ij=1gcd(i,j)n(n+1)/2

ni=1ij=1gcd(i,j)
=nd=1d(ni=1ij=1(gcd(i,j)=d))
=nd=1d(n/di=1ij=1(gcd(i,j)=1))
=nd=1dφ(n/d)
=i=1φ(i)(n/ij=1j)
=i=1φ(i)n/i(n/i+1)/2

反演2:

必要结论:
d|ndμ(n/d) = φ(n)
证明:
狄利克雷卷积形式,老套路证明。
只需要证明: d|pqdμ(pq/d)=φ(pq)
d|pqdμ(pq/d)
=qk=0pkμ(pqk)
因为要使 μ(pqk) 不为0,所以缩小循环范围。
=qk=q1pkμ(pqk)
=pqpq1
=φ(pq)
那么根据积性函数的性质可得:
所以 d|ndμ(n/d) = φ(n)

ni=1nj=1gcd(i,j)
=nd=1d(ni=1nj=1(gcd(i,j)=d))
中间莫比乌斯反演过程略。
=nd=1dn/di=1n/(di)2μ(i)
T=di ,则
=nT=1n/T2d|Tdμ(n/d)
=nT=1n/T2φ(T)

坑爹的发现:

综合反演1和反演2会得到以下式子:
ni=1n/iφ(i)=n(n+1)/2
显然我不知道它有什么用,当做一个比较吊的结论吧。

反演2Code:

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

const ll Maxn = 5000000, M = 12461987, ni_2 = 5e8 + 4, mo = 1e9 + 7;
ll p[700000], phi[Maxn + 5], h[M][2];
bool bz[Maxn + 5];

ll hash(ll x) {
    ll y = x % M;
    while(h[y][0] != 0 && h[y][0] != x)
        y = (y + 1) % M;
    return y;
}

ll dg(ll n) {
    if(n <= Maxn) return phi[n];
    ll y = hash(n);
    if(h[y][0] == n) return h[y][1];
    ll t = n % mo;
    h[y][1] = t * (t + 1) % mo * ni_2 % mo;
    fo(i, 2, n) {
        ll j = n / (n / i);
        h[y][1] -= dg(n / i) * (j - i + 1) % mo;
        i = j;
    }
    h[y][0] = n; h[y][1] = (h[y][1] % mo + mo) % mo;
    return h[y][1];
}

ll n;

int main() {
    phi[1] = 1;
    fo(i, 2, Maxn) {
        if(!bz[i]) p[++ p[0]] = i, phi[i] = i - 1;
        fo(j, 1, p[0]) {
            ll k = i * p[j];
            if(k > Maxn) break;
            bz[k] = 1;
            if(i % p[j] == 0) {
                phi[k] = phi[i] * p[j];
                break;
            }
            phi[k] = phi[i] * (p[j] - 1);
        }
    }
    fo(i, 1, Maxn) phi[i] = (phi[i - 1] + phi[i]) % mo;
    ll ans = 0;
    scanf("%lld", &n);
    fo(i, 1, n)  {
        ll j = n / (n / i), k = n / i % mo;
        k = (k * k) % mo;
        ans += k * (dg(j) - dg(i - 1) + mo) % mo;
        ans %= mo;
        i = j;
    }
    printf("%lld", ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值