P2522 [HAOI2011]Problem b
P2522 [HAOI2011]Problem b(我的第一道莫比乌斯反演)
题解:
根据题意写出函数表达式:
\(f\left ( k\right )=\sum_{i=1}^{n}\sum_{j=1}^{m}\left [ gcd\left ( i,j\right)=k\right ]\) 表示\(1\leq i\leq n,1\leq j\leq m\) , \(gcd\left ( i,j\right)=k\)成立的\(\left ( i,j\right )\)对数
令\(F\left ( x\right )=\sum_{x|d}f\left ( d\right )\),代表 \(gcd=d且是x的倍数的\left ( i,j\right )对数\);
我们知道\(x|gcd\left ( i,j\right)=\left ( x|i\right )\wedge \left ( x|j\right )\),又因为\(1\sim n之间整除x的数有\left \lfloor \frac{n}{x}\right \rfloor个\),\(1\sim m之间整除x的数有\left \lfloor \frac{m}{x}\right \rfloor个\)
所以\(F\left ( x\right )=\left \lfloor \frac{n}{x}\right \rfloor\cdot \left \lfloor \frac{m}{x}\right \rfloor\)
于是:\((F\left ( x\right )=\sum_{x|d}^{d}=\left \lfloor \frac{n}{x}\right \rfloor\cdot \left \lfloor \frac{m}{x}\right \rfloor\)
根据第二种莫比乌斯反演:则\(f\left ( x\right )=\sum_{x|d}\mu \left ( \frac{d}{x}\right )F\left ( d\right )\)
带入\(F\left ( d\right )=\left \lfloor \frac{n}{d}\right \rfloor\cdot \left \lfloor \frac{m}{d}\right \rfloor\) 得
\(f\left ( x\right )=\sum_{x|d}\mu \left ( \frac{d}{x}\right )\left \lfloor \frac{n}{d}\right \rfloor\left \lfloor \frac{m}{d}\right \rfloor\)
此时\(x=d\),\(d\leq min\left ( n,m\right )\),则\(f\left ( x\right )=\sum_{d=1}^{min\left ( n,m\right )}\mu \left ( \frac{d}{x}\right )\left \lfloor \frac{n}{d}\right \rfloor\left \lfloor \frac{m}{d}\right \rfloor\)
令\(t=\frac{d}{x}\),则\(d=xt\);\(d\leq min\left ( n,m\right )\),则\(t=\frac{d}{x}\leq min\left ( \frac{n}{x},\frac{m}{x}\right )\)
于是:\(f\left ( d\right )=\sum_{t=1}^{min\left ( \frac{n}{d},\frac{m}{d}\right )}\mu \left ( t\right )\left \lfloor \frac{n}{td}\right \rfloor\left \lfloor \frac{m}{td}\right \rfloor\)
\(d\)是题目所给的常数,这里是\(k\);
注意题目不是\(1\leq i\leq b,1\leq j\leq d\),所以还要用到容斥原理,算出\(1\sim b,1\sim d\),减去\(1\sim a,1\sim d\),再减去\(1\sim c,1\sim b\),再加上\(1\sim a,1\sim c\)
用整除分块+前缀和(预处理莫比乌斯函数)
AC_Code:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned long long ull; 5 const int maxn = 5e4+10; 6 const int mod = 2333333; 7 const int inf = 0x3f3f3f3f; 8 #define gok ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) 9 #define pii pair<int,int> 10 #define fi first 11 #define se second 12 #define pb push_back 13 #define rep(i,first,second) for(ll i=first;i<=second;i++) 14 #define dep(i,first,second) for(ll i=first;i>=second;i--) 15 #define erep(i,u) for(ll i=head[u];~i;i=e[i].nxt) 16 17 int a,b,c,d,k; 18 bool prime[maxn]; 19 int Prime[maxn],tot,mu[maxn],sum[maxn]; 20 void get_mu(){ 21 memset(prime,true,sizeof(prime)); 22 prime[0]=prime[1]=false; 23 mu[1]=1; 24 for(int i=2;i<=50000;i++){ 25 if( prime[i] ) Prime[++tot]=i,mu[i]=-1; 26 for(int j=1;j<=tot&&i*Prime[j]<=50000;j++){ 27 prime[i*Prime[j]]=false; 28 if( i%Prime[j]==0 ){ 29 mu[i*Prime[j]]=0; 30 break; 31 } 32 mu[i*Prime[j]]=-mu[i]; 33 } 34 } 35 for(int i=1;i<=50000;i++) sum[i]=sum[i-1]+mu[i]; 36 } 37 38 int cal(int n,int m){ 39 int res=0; 40 for(int i=1,j;i<=min(n,m);i=j+1){ 41 j=min(n/(n/i),m/(m/i)); 42 res += (sum[j]-sum[i-1])*(n/i)*(m/i); 43 } 44 return res; 45 } 46 47 int main() 48 { 49 int t; 50 scanf("%d",&t); 51 get_mu(); 52 // rep(i,1,10) cout<<mu[i]<<' '<<sum[i]<<endl; 53 while( t-- ){ 54 scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); 55 printf("%d\n",cal(b/k,d/k)- 56 cal((a-1)/k,d/k)- 57 cal(b/k,(c-1)/k)+ 58 cal((a-1)/k,(c-1)/k)); 59 } 60 return 0; 61 }
再放一道例题:注意下边界都是从1开始,且(a,b)(b,a)算一种,注意去重
AC_Code:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 typedef long long ll; 8 const int maxn = 1e5+10; 9 const int mod = 1e9+7; 10 const int inf = 0x3f3f3f3f; 11 #define rep(i,first,second) for(ll i=first;i<=second;i++) 12 #define dep(i,first,second) for(ll i=first;i>=second;i--) 13 14 bool prime[maxn]; 15 ll Prime[maxn],tot,mu[maxn]; 16 ll ans,sum[maxn]; 17 ll a,b,c,d,k; 18 19 void get_mu(){ 20 memset(prime,true,sizeof(prime)); 21 prime[0]=prime[1]=false; 22 mu[1]=1; 23 rep(i,2,100000){ 24 if( prime[i] ) Prime[++tot]=i,mu[i]=-1; 25 for(ll j=1;j<=tot&&i*Prime[j]<=100000;j++){ 26 prime[i*Prime[j]]=false; 27 if( i%Prime[j]==0 ){ 28 mu[i*Prime[j]]=0; 29 break; 30 } 31 mu[i*Prime[j]]=-mu[i]; 32 } 33 } 34 rep(i,1,100000) sum[i]=sum[i-1]+mu[i]; 35 } 36 37 ll cal(ll x,ll y){ 38 ll up=min(x,y); 39 ll res=0; 40 for(ll i=1,j;i<=up;i=j+1){ 41 j=min(x/(x/i),y/(y/i)); 42 res += (sum[j]-sum[i-1])*(x/i)*(y/i); 43 } 44 return res; 45 } 46 47 int main() 48 { 49 int t,cas=0; 50 scanf("%d",&t); 51 get_mu(); 52 while( t-- ){ 53 scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k); 54 if( k==0 ){ 55 printf("Case %d: 0\n",++cas); 56 continue; 57 } 58 b=b/k;d=d/k; 59 ll up=min(b,d); 60 ll ans1=cal(b,d);//重复区有两倍,要减去一倍 61 ll ans2=cal(up,up);//重复区的那两倍数算出来 62 printf("Case %d: %lld\n",++cas,ans1-ans2/2);//重复区的那两倍减去其中一倍即可 63 } 64 return 0; 65 }