题意:
给定两个区间、,,问有多少对x、y互质。
题解:
赛场上别的队都是一发A,以为很容易,搞了半天没弄出来,赛后才知道模板书上有一道一模一样的题。
不过模板书给的代码是莫比乌斯反演。
赛后百度了一下题解,发现也可以用容斥做,思路和我赛场上的思路一样,但由于赛场上写容斥的时候想到dfs,感觉复杂度太高,而且剩下时间不多了,就自动开启了自闭模式。
下面给出两种题解:
一、容斥
二、莫比乌斯反演
一、容斥
先对素数打个表,打到1e7。
这种方法计算的是两个区间中不互质的个数,然后用总数减去不互质的个数得到答案,下面分析如何计算不互质的个数。
首先我们必须知道一点:如果两个数不互质,那么这两个数唯一因式分解后,一定会有相同的素数因子,那么我们从这些素数因子入手,包含素数因子p的数可以是:p、2p、3p……,计算一下两个区间中分别包含多少个这样的数。
int t1=b/p-a/p; if(a%p==0) t1++; int t2=d/p-c/p; if(c%p==0) t2++;
t1*t2就是:因为包含素数因子p而不互质的个数。
举个例子:两个区间[3,7]和[5,12];
区间[3,7]={3,4,5,6,7};
区间[5,12]={5,6,7,8,9,10,11,12};根据素数表:{2,3,5,7,11,13……}
对于第一个素数2,2的倍数有1*2,2*2,3*2……
t1=2={4,6},t2=4={6,8,10,12},这两个集合里的数两两都不互质,并且任一集合和另一集合的补集里的数两两互质。
那么由于两个区间里的数有素因子2而不互质的个数为2*4=8个。
依次类推,之后的素数也这样计算。这种方法在计算的时候,会出现大量的重复计算,比如6=2*3,在计算素因子2的时候会把6算一遍,计算素因子3的时候又会算一遍6。这时候我们就需要用到容斥了。
重复计算的有任意两个素数的乘积、任意三个素数的乘积……
详细步骤请看代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll maxn = 1e7+5; ll prime[maxn+3]; ll num=0,ans=0; ll Max,a,b,c,d; void init() { memset(prime,0,sizeof(prime)); for(ll i=2; i<=maxn; i++) { if(!prime[i]) prime[++num]=i; for(ll j=1; j<=num&&prime[j]<=maxn/i; j++) { prime[prime[j]*i]=1; if(i%prime[j]==0) break; } } } void dfs(ll x, ll sum,ll nn) { if(nn>Max) return; if(sum>b||sum>d) return; ll t1=d/sum-c/sum; if(c%sum==0) t1++; ll t2=b/sum-a/sum; if(a%sum==0) t2++; if(nn%2==1) ans+=t1*t2; else ans-=t1*t2; for(ll i=x+1; i<=num; i++) { ll temp=sum*prime[i]; if(temp>b||temp>d) return; dfs(i,temp,nn+1); } return ; } int main() { // freopen("in.txt","r",stdin); init(); scanf("%lld%lld%lld%lld",&a,&b,&c,&d); Max=max(b,d); for(ll i=1; i<=num; i++) { if(prime[i]>b||prime[i]>d) break; dfs(i,prime[i],1); } ll ss=(d-c+1)*(b-a+1); printf("%lld",ss-ans); return 0; }
二、莫比乌斯反演
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e7; bool check[maxn+10]; int prime[maxn+10]; int mu[maxn+10]; void Moblus() { memset(check,false,sizeof(check)); mu[1] = 1; int tot = 0; for(int i = 2; i <= maxn; i++) { if(!check[i]) { prime[tot++]=i; mu[i]=-1; } for(int j = 0; j < tot; j++) { if(i*prime[j] > maxn) break; check[i*prime[j]] = true; if(i%prime[j] == 0) { mu[i*prime[j] == 0]; break; } else { mu[i*prime[j]] = -mu[i]; } } } } int sum[maxn+10]; ll solve(int n,int m) { ll ans = 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)); ans +=(ll)(sum[la]-sum[i-1])*(n/i)*(m/i); } return ans; } int main() { // freopen("in.txt","r",stdin); Moblus(); sum[0] = 0; for(int i = 1; i <= maxn ; i++) { sum[i] = sum[i-1] + mu[i]; } int a,b,c,d,k=1; scanf("%d%d%d%d",&a,&b,&c,&d); ll 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); printf("%lld",ans); return 0; }