[BZOJ2005][NOI2010]能量採集(莫比烏斯反演)
阿新 • • 發佈:2019-01-09
題目描述
題解
首先證明對於某個點(x,y),k=gcd(x,y)-1:
設gcd(x,y)=t,令x=at,y=bt,那麼在這條直線上的整數點可以表示為(a,b)(2a,2b)(3a,3b)……(x,y),由於不算x,y,則答案為gcd(x,y)-1
那麼總損耗2k+1=2×gcd(x,y)-1。
我們最終要求的式子為:
那麼我們只需要算出
推導如下:
j=1m∑d|gcd(i,j)ϕ(d)
實際上
可以用分塊來求。
需要預處理phi的字首和。
程式碼
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
const int max_n=1 e5+5;
LL n,m,ans;
LL p[max_n],phi[max_n],prime[max_n];
inline void get_phi(){
phi[1]=1;
for (int i=2;i<=n;++i){
if (!p[i]){
prime[++prime[0]]=i;
phi[i]=i-1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=n;++j){
p[i*prime[j]]=1 ;
if (i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
phi[i]+=phi[i-1];
}
}
int main(){
scanf("%lld%lld",&n,&m);
if (n>m) swap(n,m);
get_phi();
for (LL i=1,j;i<=n;i=j+1){
j=min(n/(n/i),m/(m/i));
ans+=(LL)(phi[j]-phi[i-1])*(n/i)*(m/i);
}
printf("%lld\n",ans*2-n*m);
}
總結
首先Orz ATP
求phi的時候不要打phi[prime[j]],尤其是求字首和的時候。