BZOJ 2005: [Noi2010]能量采集 莫比乌斯反演
化简一下 $\Rightarrow$$2\times \sum_{i=1}^{n}\sum_{j=1}^{m}(gcd(i,j)-1)$
稍微展开一下:
$2\times\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)-2\times\sum_{i=1}^{n}\sum_{j=1}^{m}1$
$\Rightarrow 2\times\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)-n\times m$
只需快速求出 $re=\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)$ 即可.
这个就十分好推了吧.......
$\Rightarrow \sum_{d=1}^{n}d\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==d]$
后面就是 $zap$ 了,可以很简单地求出,这里还是给一下推导:
$\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==d]$
$\Rightarrow \sum_{i=1}^{\frac{n}{d}} \sum_{j=1}^{\frac{m}{d}}[gcd(i,j)==1]$
$\Rightarrow \sum_{i=1}^{\frac{n}{d}} \sum_{j=1}^{\frac{m}{d}}\sum_
{b|i,b|j}\mu(b)$
{b|i,b|j}\mu(b)$
$\Rightarrow \sum_{b=1}^{\frac{n}{d}}\mu(b)\sum_{b|i}\sum_{b|j}$
发现 $\sum_{b|i}^{\frac{n}{d}}\sum_{b|j}^{\frac{m}{d}}=\frac{n}{db}\times\frac{m}{db}$
原式 $=\sum_{b=1}^{\frac{n}{d}}\mu(b)\frac{n}{db}\frac{m}{db}$
整除分块可在 $O(n)$ 时间内求解.
回到之前 $\sum_{d=1}^{n}d\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==d]$
定义 $calc(n,m)=\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==1]$
$\Rightarrow re=\sum_{d=1}^{n}d\times calc(\frac{n}{d},\frac{m}{d})$
这个也可以整除分块来算.
时间复杂度为 $O(n)$ 的.
最终答案为 $2\times re-n\times m$
#include<bits/stdc++.h> #define ll long long #define maxn 100104 #define M 100002 using namespace std; inline void setIO(string s) { string in=s+".in"; freopen(in.c_str(),"r",stdin); } int cnt; bool vis[maxn]; int mu[maxn], prime[maxn]; ll sumv[maxn],sum2[maxn]; inline ll calc(ll n,ll m) { ll re=0; int i,j; for(i=1;i<=n;i=j+1) { j=min(n/(n/i), m/(m/i)); re+=(sumv[j]-sumv[i-1])*(n/i)*(m/i); } return re; } inline ll solve(ll n,ll m) { ll re=0; int i,j; for(i=1;i<=n;i=j+1) { j=min(n/(n/i), m/(m/i)); re+=(sum2[j]-sum2[i-1])*calc(n/i,m/i); } return re; } int main() { // setIO("input"); int i,j,T,n,m; mu[1]=1; for(i=2;i<=M;++i) { if(!vis[i]) prime[++cnt]=i, mu[i]=-1; for(j=1;j<=cnt&&1ll*prime[j]*i<=M;++j) { vis[prime[j]*i]=1; if(i%prime[j]==0) { mu[i*prime[j]]=0; break; } mu[i*prime[j]]=-mu[i]; } } for(i=1;i<=M;++i) sumv[i]=sumv[i-1]+1ll*mu[i], sum2[i]=sum2[i-1]+1ll*i; scanf("%d%d",&n,&m); if(n>m) swap(n,m); printf("%lld\n",solve(n,m)*2-1ll*n*m); return 0; }