UVA11426 GCD(两两gcd之和各种思路)[dp筛法(容斥),欧拉函数,莫比乌斯反演]

题意

求T组\sum \sum gcd(i,j)\ (i<j)

简单-dp   中等-欧拉函数   较难-莫比乌斯反演


dp法

引我之前文章的分析:

易知1-N以为x的倍数的有\lfloor\frac{N}{x}\rfloor个,则gcd(i,j)\ (1 \leq i,j \leq N)x倍数的有\lfloor \frac{N}{x} \rfloor^2个,记为f[x]

又记g[x]表示gcd(i,j)=x\ (1 \leq i,j \leq N)的个数,g[x]=f[x]-\sum g[kx](k >1)

因为kx>N,g[x]=f[x],故可以倒着更新g[x]

最后去重就是答案

该方法时间复杂度为O(NlogN),虽然该方法对于后两题会TLE,不过其展现了大概的思路,为后面两种方法(特别是莫比乌斯反演提供了可行的方向)

#pragma GCC optimize(3)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

using LL = long long;
const int MAXN = 2e6 + 5;
LL N, ans;
LL f[MAXN], g[MAXN];

int main(){
  while(cin >> N){
    if(!N) break;
    ans = 0;
    int i, j;
    for(i = 1; i <= N; i++) f[i] = (N / i) * (N / i);
    for(i = N; i >= 1; i--){
      LL tmp = 0;
      for(j = 2 * i; j <= N; j += i) tmp += g[j];
      g[i] = f[i] - tmp;
    }
    for(i = 1; i <= N; i++) ans += i * g[i];
    ans = (ans - N * (N + 1) / 2) / 2;
    cout << ans << endl;
  }
  return 0;
}

欧拉函数

gcd(x,y)=k \ ==> gcd(\frac{x}{k}, \frac{y}{k})=1,若考虑x<y那么针对给定y,k对其贡献为\phi (\frac{y}{k} ) (y!=k, k|y)

g[j]=\sum_{i=1}^{j-1}gcd(i,j),那么g[j]=\sum_{d|j} d\phi (\frac{j}{d}),那么对于询问G[N]=\sum_{i=1}^N g[i]

通过O(NlogN)即可以针对询问进行查询(针对第二题的大批量询问)

 

莫比乌斯反演

回到dp的分析,我们针对gcd(i,j)x倍数的f[x]gcd(i,j)=xg[x]进行反演

\\ f[x]=\sum_{x|d} g[d] = [\frac{N}{x}]^2\\ => \ g[x]=\sum_{x|d}\mu (\frac{d}{x})f[d]\\ => ans=\sum xg[x]=\sum x \sum_{i=1}^{[\frac{N}{x}]}\mu (i)f[ix](这里要整数分块求和,具体解释可以看看整体约数求和)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

using LL = long long;
const int MAXN = 5e6 + 5;
LL N;
int mu[MAXN], phi[MAXN], pre[MAXN], prime[MAXN], cnt_prime;
bool noprime[MAXN];
void sieve(int top);
LL g[MAXN], G[MAXN];
LL solve(int);

int main(){
  sieve(3e5);
  int i, j;
  for(i = 1; i <= 3e5; i++)
    for(j = i + i; j <= 3e5; j += i)
      g[j] += i * phi[j / i];
  for(i = 1; i <= 3e5; i++) G[i] = G[i - 1] + g[i];
  while(cin >> N && N){
    //Euler_fuc
    cout << G[N] << endl;

    //Mobius inversion
    int i;
    LL ans = 0;
    for(i = 1; i <= N; i++)
      ans += i * solve(N / i);

    //optimize
    int l, r;
    LL ans = 0;
    for(l = 1; l <= N; l = r + 1){
      r = N / (N / l);
      ans += solve(N / l) * (r + l) * (r - l + 1) / 2;
    }

    ans = (ans - N * (N + 1) / 2) / 2;
    cout << ans << endl;
  }
  return 0;
}

LL solve(int n){
  LL res = 0, l, r;
  for(l = 1; l <= n; l = r + 1)
    r = n / (n / l), res += (pre[r] - pre[l - 1]) * (n / l) * (n / l);
  return res;
}

void sieve(int top){
  mu[1] = 1, phi[1] = 1;
  int i, j;
  for(i = 2; i <= top; i++){
    if(!noprime[i]) prime[++cnt_prime] = i, mu[i] = -1, phi[i] = i - 1;
    for(j = 1; j <= cnt_prime && prime[j] * i <= top; j++){
      noprime[prime[j] * i] = true;
      if(i % prime[j] == 0){
        mu[prime[j] * i] = 0;
        phi[prime[j] * i] = phi[i] * prime[j];
        break;
      }else{
        mu[i * prime[j]] = -mu[i];
        phi[i * prime[j]] = phi[i] * (prime[j] - 1);
      }
    }
  }
  for(i = 1; i <= top; i++) pre[i] = pre[i - 1] + mu[i];
}

特别的单次询问这种方法可以做到O(N)

但如果多次询问则需要进一步化解

\\ ans=\sum x \sum_{i=1}^{[\frac{N}{x}]}\mu (i)f[ix] = \sum x \sum_{i=1}^{[\frac{N}{x}]}\mu (i)[\frac{N}{ix}][\frac{N}{ix}]\\ =\sum_{x=1}^{N}\sum_{i=1}^{[\frac{N}{x}]}x\mu (i)[\frac{N}{ix}][\frac{N}{ix}] =\sum_{j=1}^{N}[\frac{N}{j}][\frac{N}{j}]\sum_{x|j}x\mu (\frac{j}{x})

通过O(NlogN)处理到最内层的前缀和则可以做到每次询问O(\sqrt N)

  for(i = 1; i <= top; i++)
    for(j = i; j <= top; j += i)
      sum[j] += mu[j / i] * i;
  for(i = 1; i <= top; i++) sum[i] += sum[i - 1];
  int l, r; LL ans = 0;
  for(l = 1; l <= top; l = r + 1){
    r = min(N / (N / l), M / (M / l));
    ans += (LL)(N / l) * (M / l) * (sum[r] - sum[l - 1]);
  }

PS:这里g[x]还有一种计算方式,利用欧拉函数g[x]=\sum_{i=2}^{[\frac{N}{x}]}\phi (i)(这里和上述欧拉函数中g[x]意义不同(两种角度))

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值