bzoj2005 能量采集 gcd 容斥

ans = sigma_x(sigma_y(gcd(x,y) * 2 - 1)),1<=x<=n,1<=y<=m

枚举x,y,O(nmlogn),超时

换个角度,枚举d = gcd(x,y)

d对ans的贡献为2*d-1

若有n个(x,y)使得gcd(x,y) = d,则贡献为n * (2 * d - 1)

f(d) 表示gcd(x,y) = d 的(x,y)个数

ans = sigma(f[d] * (2 * d - 1)),1 <= d <= min(n,m)

那么问题就是求f(d)

g(d) 表示d|gcd(x,y)的(x,y)个数

则g(d) = (n / d) * (m / d)

f(d) = g(d) - f(i * d) , 2 <= i <= min(n,m) / d

反序遍历d,即min(n,m) 到 1

求f(d),累加f(d) * (2 * d - 1) 到ans

O(nlogn)

 

                                            
  //File Name: bzoj2005.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年02月12日 星期五 14时08分18秒
                                   

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

#define LL long long

using namespace std;

const int MAXN = 100000+5;

LL f[MAXN];

void solve(int n,int m)
{
    LL ans = 0LL;
    int mi = min(n,m);
    memset(f,0,sizeof f);
    for(int i=mi;i>0;i--){
        f[i] = (LL)(n/i) * (m/i);
        for(int j=2;j<=mi/i;j++){
            f[i] -= f[j*i];
        }
        ans += f[i] * (i * 2LL - 1);
    }

    cout << ans << endl;
    return ;
}

int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m)){
        solve(n,m);
    }
    return 0;
}

 

posted on 2016-06-03 17:13  _fukua  阅读(197)  评论(0编辑  收藏  举报