[bzoj2301: [HAOI2011]Problem b] 乞讨

</pre><pre code_snippet_id="507886" snippet_file_name="blog_20141104_2_5383199" name="code" class="cpp">#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <cstdio>
#include <cstring>
using namespace std;

typedef long long LL;

inline int read(){
    int x = 0,f = 1; char ch = getchar();
    while(ch < '0'||ch > '9'){if(ch == '-')f=-1;ch = getchar();}
    while(ch >= '0'&&ch <= '9'){x = x * 10 + ch -'0';ch = getchar();}
    return x*f;
}

//////////////////////////////////////////////////////////////////

/*
算法:容斥原理 + 分块
题目:
  对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,
  且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
  
  1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

*/

const int MAXN = 50000 + 10;
int tot;
LL mu[MAXN+1],sum[MAXN+1],pri[MAXN+1];
bool mark[MAXN];

void get(){
   mu[1] = 1;
   for(int i = 2;i <= MAXN;++i){
      if(!mark[i])pri[tot++] = i,mu[i] = -1;
      for(int j = 0;j < tot&&i*pri[j] <= MAXN;++j){
        mark[i*pri[j]] = 1;
        if(i % pri[j]==0){mu[i*pri[j]] = 0; break;}
        else mu[i*pri[j]] = -mu[i];
      }
   }

   for(int i = 1;i <= MAXN;++i) //预处理前缀
    sum[i] = sum[i-1] + mu[i];
}

int cal(int n,int m){
    if(n > m) swap(n,m);
    LL ans = 0,pos;
    for(LL i = 1;i <= n;i = pos + 1){
        pos = min(n/(n/i),m/(m/i));       //分块
        ans += (sum[pos] - sum[i-1]) * (n/i) * (m/i);
    }
    return ans;
}

int main()
{
    get();
    int T = read();
    while(T--){
        int a = read(),b = read(),c = read(),d = read(),k = read();
        LL ans = cal(b/k,d/k);
        ans -= cal((a-1)/k,d/k);
        ans -= cal(b/k,(c-1)/k);
        ans += cal((a-1)/k,(c-1)/k);
        printf("%lld\n",ans);
    }
    return 0;
}

版权声明:本文博主原创文章。博客,未经同意不得转载。

posted @ 2015-09-27 15:09  hrhguanli  阅读(142)  评论(0编辑  收藏  举报