BZOJ2005 [Noi2010]能量采集(洛谷P1447)

莫比乌斯反演/乱搞

BZOJ题目传送门
洛谷题目传送门

题目要我们求2i=1nj=1m(i,j)nm

主要是求i=1nj=1m(i,j)

我们设f(x)=i=1nj=1m[(i,j)=x],F(x)=i=1nj=1m[x|(i,j)]

那么原式=x=1min(n,m)xf(x)

很容易发现F(x)=nxmx

有两种做法通过F(x)f(x)

一:乱搞。发现f(x)=F(x)f(kx)(k>1),那么从后往前推求出f(x)即可。

二:反演。套公式可得f(x)=x|dF(d)μ(d/x),直接枚举即可。

代码:

//乱搞
#include<cstdio>
#include<algorithm>
#define N 100005
using namespace std;
typedef long long LL;
LL f[N],ans,n,m;
int main(){
    scanf("%lld%lld",&n,&m);
    if (n>m) swap(n,m);
    for (int i=n;i;i--){
        f[i]=(n/i)*(m/i);
        for (int j=i<<1;j<=n;j+=i) f[i]-=f[j];
        ans+=((i<<1)-1)*f[i];
    }
    return printf("%lld\n",ans),0;
}
//反演
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
typedef long long LL;
int n,m,mu[N],p[N];
LL ans;
bool f[N];
inline void mkp(){
    mu[1]=1;
    for (int i=2;i<=n;i++){
        if (!f[i]) p[++p[0]]=i,mu[i]=-1;
        for (int j=1,v;j<=p[0]&&(v=i*p[j])<=n;j++){
            f[v]=true,mu[v]=-mu[i];
            if (!(i%p[j])) { mu[v]=0; break; }
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    if (n>m) swap(n,m); mkp();
    for (int i=1;i<=n;i++)
        for (int j=i;j<=n;j+=i)
            ans+=1ll*mu[j/i]*(1ll*n/(1ll*j))*(1ll*m/(1ll*j))*((i<<1)-1);
    return printf("%lld\n",ans),0;
}
发布了459 篇原创文章 · 获赞 477 · 访问量 11万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览