bzoj 2301: [HAOI2011]Problem b

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)的个数

解题报告:
这题其实和上题差不多,只需要容斥一波即可,如何反演参见上题,此题中不能暴力统计答案,需要一个优化.
观察到\(G_n=(a/i)*(b/i)\)这个式子,很多时候i变化,但(a/i)和(b/i)不会变化,其实(a/i)只有\(\sqrt{a}\)种取值,所以我们我们可以直接跳着统计,此时记一个\(\mu\)的前缀和即可

复杂度:\(O(n\sqrt{n})\)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=50005;
int num=0,prime[N],phi[N],sum[N];bool vis[N];
void prework(){
	int to;phi[1]=1;
	for(int i=2;i<N;i++){
		if(!vis[i]){
			prime[++num]=i;
			phi[i]=-1;
		}
		for(int j=1;j<=num && prime[j]*i<N;j++){
			to=prime[j]*i;vis[to]=true;
			if(i%prime[j])phi[to]=-phi[i];
			else{
				phi[to]=0;
				break;
			}
		}
	}
	for(int i=1;i<N;i++)sum[i]=sum[i-1]+phi[i];
}
ll solve(int a,int b,int k){
	a/=k;b/=k;if(a>b)swap(a,b);
	ll ret=0,nxt;
	for(int i=1;i<=a;i=nxt+1){
		nxt=Min(a/(a/i),b/(b/i));
		ret+=(ll)(a/i)*(b/i)*(sum[nxt]-sum[i-1]);
	}
	return ret;
}
void work()
{
	int a,b,c,d,k;
	scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
	ll ans=solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k);
	printf("%lld\n",ans);
}

int main()
{
	int T;cin>>T;
	prework();
	while(T--)work();
	return 0;
}

posted @ 2017-09-02 01:18  PIPIBoss  阅读(...)  评论(...编辑  收藏