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

莫比乌斯反演/乱搞

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

题目要我们求 2ni=1mj=1(i,j)nm 2 ∑ i = 1 n ∑ j = 1 m ( i , j ) − n m

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

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

那么原式 =min(n,m)x=1xf(x) = ∑ x = 1 m i n ( n , m ) x ∗ f ( x )

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

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

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

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

代码:

//乱搞
#include
#include
#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
#include
#include
#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;
}

你可能感兴趣的:(洛谷,BZOJ,蒟蒻zxl的Blog专栏,BZOJ,洛谷,莫比乌斯反演)