bzoj 2005 & 洛谷 P1447 [ Noi 2010 ] 能量采集 —— 容斥 / 莫比烏斯反演
題目:bzoj 2005 https://www.lydsy.com/JudgeOnline/problem.php?id=2005
洛谷 P1447 https://www.luogu.org/problemnew/show/P1447
首先,題意就是求 ∑(1 <= i <= n) ∑(1 <= j <= m) [ 2 * gcd(i,j) -1 ];
方法1:容斥原理
枚舉每個數作為 gcd 被算了幾次;
對於 d ,算的次數 f[d] 就是 n/d 和 m/d 中互質的數的對數;
所有數對的個數是 (n/d) * (m/d),減去其中 gcd 是2,3……的數對個數,這裏就可以用到之前算出來的答案;
比如要減去這之中 gcd 是2的數對,那麽減去 f[d/2] 即可,而且因為定義原因不會重復減;
然後 *2 -1 即可。
代碼如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const maxn=1e5+5; int n,m; ll f[maxn],ans; int main() { scanf("%d%d",&n,&m);if(n>m)swap(n,m); for(int g=n;g;g--) { f[g]=(ll)(n/g)*(m/g);//(ll) //加括號!因為要下取整 for(int i=2;i*g<=n;i++)f[g]-=f[i*g]; // ans+=2*tmp; ans+=(g*2-1)*f[g]; } printf("%lld\n",ans); return 0; }
方法2:莫比烏斯反演
原式 ∑(1 <= i <= n) ∑(1 <= j <= m) [ 2 * gcd(i,j) - 1 ] ( n = min(m,n) )
由於 n = ∑ ( d|n ) φ(d) (感性理解:約分……)
所以原式變成 ∑(1 <= i <= n) ∑(1 <= j <= m) [ 2 * ∑ ( d|i , d|j ) φ(d) - 1]
把 d 提前,-1提出去 ∑( 1 <= d <= n ) [ 2 * φ(d) * ∑(1 <= i <= n|d ) ∑(1 <= j <= m|d ) 1 ] - n*m
也就是 2 * ∑( 1 <= d <= n ) [ φ(d) * (n/d) * (m/d) ] - n*m
註意 φ(1) = 1!
代碼如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const maxn=1e5+5; int n,m,p[maxn],phi[maxn],cnt; ll ans; void init() { phi[1]=1;//! for(int i=2;i<=n;i++) { if(!phi[i])p[++cnt]=i,phi[i]=i-1; for(int j=1;j<=cnt&&i*p[j]<=n;j++) { phi[i*p[j]]=phi[i]*(p[j]-1); if(i%p[j]==0)phi[i*p[j]]=phi[i]*p[j]; } } } int main() { scanf("%d%d",&n,&m); if(n>m)swap(n,m); init(); for(int g=1;g<=n;g++) ans+=2*(ll)phi[g]*(n/g)*(m/g); ans-=(ll)n*m; printf("%lld\n",ans); return 0; }
bzoj 2005 & 洛谷 P1447 [ Noi 2010 ] 能量采集 —— 容斥 / 莫比烏斯反演