bzoj 2301: [HAOI2011] Problem b

Time Limit: 50 Sec  Memory Limit: 256 MB
Submit: 2833  Solved: 1256
[Submit][Status][Discuss]

Description

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

Input

第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k

Output

共n行,每行一个整数表示满足要求的数对(x,y)的个数

Sample Input

2
2 5 1 5 1
1 5 1 5 2

Sample Output

14
3

HINT

100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

题解:

  首先先放个PoPoQQQ的ppt其次再来份题解。(题解中的容斥定理把c和b写反了,无视就好)

  总结一下,莫比乌斯反演有两种形式

  形式一:

            

  形式二:

          

  本题用到的是第二种形式,题目要求求x∈[a,b],y∈[c,d]。x和y满足gcd(x,y)==k的x和y的数对数。

  令f(i)为x∈[1,N],y∈[1,M],gcd(x,y)==i 的(x,y)数对数。

  令F(i)为x∈[1,N],y∈[1,M],i|gcd(x,y) 的(x,y)数对数。

  可以感觉,a和c的限制有些不好处理,不妨先忘掉a,c的限制。再看形式二,考虑f(i)与F(i)的关系,f(i)是gcd(x,y)==i,F(i)是gcd(x,y)==t*i(t∈Z),所以F(i)=sigma(f(d))(i|d),所以f(i)与F(i)正好满足形式二的第一个,可以利用莫比乌斯反演用F(i)表示f(i)。而F[i]=[N/i]*[M/i]  ([]表示向下取整)

  再考虑a和c的限制,设g(x,y,k)表示[1,x],[1,y] gcd(x1,y1)==k的对数,这个式子其实等价于g(x/k,y/k,1)。

  再有容斥原理可得:Ans=g(b/k,d/k,1)-g((a-1)/k,d/k,1)-g(c/k,b/k,1)+g((a-1)/k,(c-1)/k,1),对于每个g(,,)我们都可以用f()来求。

  O(n)的复杂度不足以解决此题,还有一个O(sqrt(n))的优化,具体看ppt或题解,讲的很清楚。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<vector>
 8 #include<queue>
 9 using namespace std;
10 typedef long long LL; 
11 const LL maxn=50000; 
12 LL mu[maxn],prime[maxn],sum[maxn],tot,N;
13 bool not_prime[maxn];
14 LL a,b,c,d,k,ANS;
15 inline void pre(){
16     memset(not_prime,false,sizeof(not_prime));
17     mu[1]=1;
18     for(LL i=2;i<=maxn;i++){
19         if(not_prime[i]==false){
20             prime[++tot]=i;
21             mu[i]=-1;
22         }
23         for(LL j=1;j<=tot;j++){
24             if(i*prime[j]>maxn) break;
25             not_prime[prime[j]*i]=true;
26             if(i%prime[j]==0){
27                 mu[prime[j]*i]=0;
28                 break;
29             }
30             else mu[prime[j]*i]=-mu[i];
31         }
32     }
33 }
34 inline LL solve(LL N,LL M){
35     LL ans=0;
36     if(N>M) swap(N,M);
37     for(LL i=1,la=0;i<=N;i=la+1){
38         la=min(N/(N/i),M/(M/i));
39         ans+=(sum[la]-sum[i-1])*(N/i)*(M/i);
40     }
41     return ans;
42 } 
43 int main(){
44 //    freopen("b.in","r",stdin);
45 //    freopen("b.out","w",stdout);
46     pre();
47     sum[0]=0;
48     for(LL i=1;i<=maxn;i++){
49         sum[i]=sum[i-1]+mu[i];
50     }
51     scanf("%d",&N);
52     while(N--){
53         scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
54         ANS=solve(b/k,d/k)-solve((a-1)/k,d/k)-solve(b/k,(c-1)/k)+solve((a-1)/k,(c-1)/k);
55         printf("%lld\n",ANS);
56     }
57     return 0;
58 }

 

 

 

 

posted @ 2016-03-11 10:36  CXCXCXC  阅读(261)  评论(0编辑  收藏  举报