P2522 [HAOI2011]Problem b(我的第一道莫比乌斯反演)
题解:
根据题意写出函数表达式:
(fleft ( k ight )=sum_{i=1}^{n}sum_{j=1}^{m}left [ gcdleft ( i,j ight)=k ight ]) 表示(1leq ileq n,1leq jleq m) , (gcdleft ( i,j ight)=k)成立的(left ( i,j ight ))对数
令(Fleft ( x ight )=sum_{x|d}fleft ( d ight )),代表 (gcd=d且是x的倍数的left ( i,j ight )对数);
我们知道(x|gcdleft ( i,j ight)=left ( x|i ight )wedge left ( x|j ight )),又因为(1sim n之间整除x的数有left lfloor frac{n}{x} ight floor个),(1sim m之间整除x的数有left lfloor frac{m}{x} ight floor个)
所以(Fleft ( x ight )=left lfloor frac{n}{x} ight floorcdot left lfloor frac{m}{x} ight floor)
于是:((Fleft ( x ight )=sum_{x|d}^{d}=left lfloor frac{n}{x} ight floorcdot left lfloor frac{m}{x} ight floor)
根据第二种莫比乌斯反演:则(fleft ( x ight )=sum_{x|d}mu left ( frac{d}{x} ight )Fleft ( d ight ))
带入(Fleft ( d ight )=left lfloor frac{n}{d} ight floorcdot left lfloor frac{m}{d} ight floor) 得
(fleft ( x ight )=sum_{x|d}mu left ( frac{d}{x} ight )left lfloor frac{n}{d} ight floorleft lfloor frac{m}{d} ight floor)
此时(x=d),(dleq minleft ( n,m ight )),则(fleft ( x ight )=sum_{d=1}^{minleft ( n,m ight )}mu left ( frac{d}{x} ight )left lfloor frac{n}{d} ight floorleft lfloor frac{m}{d} ight floor)
令(t=frac{d}{x}),则(d=xt);(dleq minleft ( n,m ight )),则(t=frac{d}{x}leq minleft ( frac{n}{x},frac{m}{x} ight ))
于是:(fleft ( d ight )=sum_{t=1}^{minleft ( frac{n}{d},frac{m}{d} ight )}mu left ( t ight )left lfloor frac{n}{td} ight floorleft lfloor frac{m}{td} ight floor)
(d)是题目所给的常数,这里是(k);
注意题目不是(1leq ileq b,1leq jleq d),所以还要用到容斥原理,算出(1sim b,1sim d),减去(1sim a,1sim d),再减去(1sim c,1sim b),再加上(1sim a,1sim 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 ",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 ",++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 ",++cas,ans1-ans2/2);//重复区的那两倍减去其中一倍即可 63 } 64 return 0; 65 }