• [NOI2010]能量采集(莫比乌斯反演)


    题面: bzoj luogu

    NOI2010能量采集 题解

    读完题之后我们发现在每个产生贡献的点((x1,y1))中,它与原点之间的点((x2,y2))都满足(x2|x1),(y2|y1)。现在我们要求它与原点之间点的个数,也就是这个点((x,y))最大可以被除以多少——肯定是(gcd(x1,y1))啊。

    所以我们就知道怎么做啦:(2 imes sum_{i=1}^n imes sum_{j=1}^m imes gcd(i,j)-n imes m)
    中间的那个可以用莫比乌斯反演做!

    设f(i)表示x,y最大公约数为i的个数。设F(i)表示x,y存在i这个公约数。

    因为(f(i)=sum_{i|j} imes F(j)),上限为(min(n,m))

    所以(F(i)=sum_{i|j}mu(j/i) imes f(j))

    f(i)不太好做,但是F(i)却很容易算出来(具体怎么算大家可以参考HDU GCD这个题)

    所以我们预处理出(mu)函数的值,然后按照套路做就可以啦qwq

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define MAXN 100010
    using namespace std;
    int n,m,cnt;
    int vis[MAXN],mu[MAXN],prime[MAXN];
    long long ans;
    long long f[MAXN],F[MAXN];
    inline void get_mu()
    {
        vis[1]=mu[1]=1;
        for(int i=2;i<=MAXN;i++)
        {
            if(vis[i]==0) mu[i]=-1,prime[++cnt]=i;
            for(int j=1;j<=cnt&&i*prime[j]<=MAXN;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]) mu[i*prime[j]]=-mu[i];
                else {mu[i*prime[j]]=0;break;}
            }
        }
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d%d",&n,&m);
        get_mu();
        if(n>m) swap(n,m);
        for(int i=1;i<=n;i++) F[i]=1ll*(n/i)*(m/i);
        for(int i=1;i<=n;i++) 
            for(int j=i;j<=n;j+=i)
                f[i]+=1ll*mu[j/i]*F[j];
        for(int i=1;i<=n;i++) ans+=1ll*f[i]*i;
        ans=ans*2-1ll*n*m;
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    泛型编程 --迭代器
    cpp输入输出加速
    算法训练 加法运算(指针的一个测试)
    蓝桥杯-基础练习-字母图形
    蓝桥杯-基础练习-特殊回文数
    win10下稍微美观下Git
    mysql8.0以上版本注册驱动并建立数据库的连接公共代码
    idea使用的一些问题解决记录
    单链表逆转(递归指针实现)
    increment/decrement/dereference操作符
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10160106.html
Copyright © 2020-2023  润新知