1. 程式人生 > >BZOJ 2301 莫比烏斯反演入門

BZOJ 2301 莫比烏斯反演入門

思想 phi 問題 ace tail main pro html 簡單的

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 Sample Output 14
3
此題作為我的莫比烏斯反演的入門題 推薦文章     https://wenku.baidu.com/view/fbec9c63ba1aa8114431d9ac.html 學習莫比烏斯反演
    https://wenku.baidu.com/view/fbe263d384254b35eefd34eb.html
    http://blog.csdn.net/outer_form/article/details/50590197 簡單的說下莫比烏斯反演的作用   對於一個函數f(n) 我們很難直接求出它的值,但是我可以求出倍數和或者約束和F(n),那麽我們就可以將F通過莫比烏斯反演來得到f,基於容斥思想   莫比烏斯反演常用於處理一些gcd的問題 代碼如下:
#include <bits/stdc++.h>

using namespace std;
typedef long long
ll; typedef long long LL; const int maxn = 5e4+5000; int p[maxn],mo[maxn],phi[maxn],cnt,sum[maxn]; int a,b,c,d,k; bool vis[maxn]; void init() { mo[1]=1; phi[1]=1; for(int i=2;i<=maxn-10;i++){ if(!vis[i]){ mo[i]=-1; phi[i]=i-1; p[cnt++]=i; }
for(int j=0;j<cnt&&(ll)i*p[j]<=maxn-10;j++){ vis[i*p[j]]=true; if(i%p[j]==0){ mo[i*p[j]]=0; phi[i*p[j]]=phi[i]*p[j]; break; } mo[i*p[j]]=-mo[i]; phi[i*p[j]]=phi[i]*(p[j]-1); } } } ll solve (int n,int m) { ll ret = 0; if (n>m) swap(n,m); for (int i=1,la=0;i<=n;i=la+1){ la = min(n/(n/i),m/(m/i)); ret+=(long long)(sum[la]-sum[i-1])*(n/i)*(m/i); } return ret; } int main() { //freopen("de.txt","r",stdin); init(); int T; for (int i=1;i<=50000;++i) sum[i] = sum[i-1] + mo[i]; scanf("%d",&T); while (T--){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); ll ans = solve(b/k,d/k)-solve((a-1)/k,d/k)-solve((c-1)/k,b/k)+solve((a-1)/k,(c-1)/k); printf("%lld\n",ans); } return 0; }

BZOJ 2301 莫比烏斯反演入門