[HAOI2011]Problem b 题解

题目大意:

  对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y)=k。

思路:

  设f(k)为当1≤x≤n,1≤y≤m,且n≤m,使gcd(x,y)=k的数对(x,y)的对数,g(k)为当1≤x≤n,1≤y≤m,且n≤m,使k|gcd(x,y)的数对(x,y)的对数。,莫比乌斯反演,得会有连续的一段相同且相同的为一定连续的一段,可证最多有2√n和2√m段,分块处理,对于每个询问可O(√n)解决。

 

代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int M=50009;
 5 int k,prime[M],mu[M],s[M];
 6 bool flag[M];
 7 
 8 int read()
 9 {
10     int x=0; char ch=getchar();
11     while (ch<'0' || ch>'9') ch=getchar();
12     while (ch>='0' && ch<='9') x=(x<<1)+(x<<3)+ch-48,ch=getchar();
13     return x;
14 }
15 
16 void getmu(int n)
17 {
18     mu[1]=1;
19     int i,j,k,cnt=0;
20     for (i=2;i<n;++i)
21     {
22         if (!flag[i]) prime[++cnt]=i,mu[i]=-1;
23         for (j=1;j<=cnt && (k=i*prime[j])<n;++j)
24         {
25             flag[k]=1;
26             if (!(i%prime[j])) { mu[k]=0; break; }
27             mu[k]=-mu[i];
28         }
29     }
30     for (i=1;i<n;++i) s[i]=s[i-1]+mu[i];
31 }
32 
33 int sum(int n,int m)
34 {
35     if (n>m) swap(n,m);
36     n=n/k,m=m/k;
37     int i,j,ans=0;
38     for (i=1;i<=n;i=j+1)
39     {
40         j=min(n/(n/i),m/(m/i));
41         ans=ans+(s[j]-s[i-1])*(n/i)*(m/i);
42     }
43     return ans;
44 }
45 
46 int main()
47 {
48     getmu(M);
49     for (int T=read();T;--T)
50     {
51         int a=read(),b=read(),c=read(),d=read();k=read();
52         printf("%d\n",sum(b,d)-sum(a-1,d)-sum(c-1,b)+sum(a-1,c-1));
53     }
54     return 0;
55 }

 

 

 

 

 

posted @ 2017-01-20 15:15  HHshy  阅读(227)  评论(0编辑  收藏  举报