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 }

 

posted @ 2020-04-27 14:02  swsyya  阅读(135)  评论(0编辑  收藏  举报

回到顶部