bzoj 2005: [Noi2010]能量采集【莫比烏斯反演】
阿新 • • 發佈:2018-09-19
can str isp code left sum += name swa
分塊求即可
註意到k=gcd(x,y)-1,所以答案是
\[
2*(\sum_{i=1}^{n}\sum_{i=1}^{m}gcd(i,j))-n*m
\]
去掉前面的乘和後面的減,用莫比烏斯反演來推,設n<m:
\[
\sum_{i=1}^{n}\sum_{i=1}^{m}gcd(i,j)
\]
\[
\sum_{d=1}^{n}d*\sum_{i=1}^{n}\sum_{i=1}^{m}[gcd(i,j)==d]
\]
\[
\sum_{d=1}^{n}d*\sum_{i=1}^{\frac{n}{d}}\sum_{i=1}^{\frac{m}{d}}[gcd(i,j)==1]
\]
\[
\sum_{d=1}^{n}d*\sum_{g=1}^{\frac{n}{d}}\mu(g)\left \lfloor \frac{n}{dg} \right \rfloor\left \lfloor \frac{m}{dg} \right \rfloor
\]
分塊求即可
#include<iostream> #include<cstdio> using namespace std; const int N=100005; long long n,m,mb[N],s[N],q[N],tot,ans; bool v[N]; long long mobi(long long n,long long m) { long long r=0ll; for(long long i=1,la;i<=n;i=la+1) { long long ni=n/i,mi=m/i; la=min(n/ni,m/mi); r+=(s[la]-s[i-1])*ni*mi; } return r; } int main() { scanf("%lld%lld",&n,&m); if(n>m) swap(n,m); mb[1]=1; for(long long i=2;i<=n;i++) { if(!v[i]) { mb[i]=-1; q[++tot]=i; } for(long long j=1;j<=tot&&q[j]*i<=n;j++) { long long k=q[j]*i; v[k]=1; if(i%q[j]==0) { mb[k]=0; break; } mb[k]=-mb[i]; } } for(long long i=1;i<=n;i++) s[i]=s[i-1]+mb[i]; for(long long i=1,la;i<=n;i=la+1) { long long ni=n/i,mi=m/i; la=min(m/mi,n/ni); ans+=(i+la)*(la-i+1)/2ll*mobi(ni,mi); } printf("%lld",2*ans-n*m); return 0; }
bzoj 2005: [Noi2010]能量采集【莫比烏斯反演】