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

莫比乌斯反演/乱搞

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

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

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

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

那么原式 = x = 1 m i n ( n , m ) x f ( x )

很容易发现 F ( x ) = n x m x

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

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

二:反演。套公式可得 f ( x ) = x | d F ( 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;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/80942852