BZOJ 2301: [HAOI2011]Problem b(莫比乌斯反演)

http://www.lydsy.com/JudgeOnline/problem.php?id=2301

题意:
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

 

思路:

先简单介绍一下莫比乌斯反演在数论中的作用:

 

那么怎么做这道题呢?

 

接下来我们只需要枚举d就可以了,但是这里还有一个可以优化的地方,我们依次+1枚举d的时候,有时候n/d和m/d是不会改变的,比如说现在n=m=,那么d=3,4,5时n/d和m/d都是不变的,这样一来的话我们可以分块处理,需要计算一下莫比乌斯的前缀和,就可以将3,4,5的值一起计算了,这样一来,枚举的数量将大大减小。具体看代码。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<sstream>
 6 #include<vector>
 7 #include<stack>
 8 #include<queue>
 9 #include<cmath>
10 #include<map>
11 #include<set>
12 using namespace std;
13 typedef long long ll;
14 typedef pair<int,int> pll;
15 const int INF = 0x3f3f3f3f;
16 const int maxn = 1e5 + 5;
17 
18 int a, b, c, d, k;
19 
20 bool check[maxn];
21 int prime[maxn];
22 int mu[maxn];
23 int sum[maxn];
24 
25 void Moblus()
26 {
27     memset(check, false, sizeof(check));
28     mu[1] = 1;
29     int tot = 0;
30     for (int i = 2; i <= maxn; i++)
31     {
32         if (!check[i])
33         {
34             prime[tot++] = i;
35             mu[i] = -1;
36         }
37         for (int j = 0; j < tot; j++)
38         {
39             if (i * prime[j] > maxn)
40             {
41                 break;
42             }
43             check[i * prime[j]] = true;
44             if (i % prime[j] == 0)
45             {
46                 mu[i * prime[j]] = 0;
47                 break;
48             }
49             else
50             {
51                 mu[i * prime[j]] = -mu[i];
52             }
53         }
54     }
55     return ;
56 }
57 
58 int solve(int n, int m)
59 {
60     if(n>m)  swap(n,m);
61     int ans=0;
62 
63     for(int i=1,last=0;i<=n;i=last+1)
64     {
65         last=min(n/(n/i),m/(m/i));  //分块处理
66         ans+=(sum[last]-sum[i-1])*(n/i)*(m/i);
67     }
68     return ans;
69 }
70 
71 
72 int main()
73 {
74     //freopen("in.txt","r",stdin);
75     int T;
76     Moblus();
77     sum[0]=0;
78     for(int i=1;i<=maxn;i++)  sum[i]=sum[i-1]+mu[i];
79 
80     scanf("%d",&T);
81     while(T--)
82     {
83         scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
84         printf("%d\n",solve(b/k,d/k)-solve(b/k,(c-1)/k)-solve((a-1)/k,d/k)+solve((a-1)/k,(c-1)/k));
85     }
86     return 0;
87 }

 

posted @ 2017-07-28 15:02  Kayden_Cheung  阅读(181)  评论(0编辑  收藏  举报
//目录