BZOJ 2301: [HAOI2011]Problem b 2045: 双亲数 同BZOJ 1101 Zap 莫比乌斯反演 3倍经验
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)的个数
Sample Input
2
2 5 1 5 1
1 5 1 5 2
1 5 1 5 2
Sample Output
14
3
思路 :
在我Zap的博客里有具体实现 2301就是相当于矩阵前缀和的那种处理, 把不满足条件的减掉就好辣!
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> const int N = 51000; bool np[N]; int pr[N], tot, mu[N], sum[N]; using namespace std; void init() { mu[1]=1; sum[1]=1; for(int i=2;i<N;i++) { if(!np[i]) { pr[++tot]=i; mu[i] = -1; } for(int j=1;j<=tot&&i*pr[j]<N;j++) { np[i*pr[j]]=1; if(i % pr[j] == 0) { mu[i*pr[j]] = 0; break; } mu[i*pr[j]]=-mu[i]; } sum[i] = sum[i-1] + mu[i]; } } long long work(int a,int b,int d) { a/=d; b/=d; if(a>b)swap(a,b); int lst = 0; long long ans=0; for(int p=1;p<=a;p=lst+1) { lst=min(a/(a/p),b/(b/p)); ans+=(long long)(sum[lst]-sum[p-1])*(a/p)*(b/p); } return ans; } int main() { int t,a,b,c, d, k; init(); //for(int i=1;i<=50000;i++) printf("%d\n",sum[i]); scanf("%d",&t); while(t--) { scanf("%d%d%d%d%d",&a,&b,&c, &d, &k); a--, c--; long long ans1 = work(b,d,k); ans1-= work(a, d, k); ans1-= work(b, c, k); ans1+= work(a, c, k); printf("%lld\n", ans1); } }