bzoj 2301: [HAOI2011] Problem b
Submit: 2833 Solved: 1256
[Submit][Status][Discuss]
Description
Input
第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k
Output
共n行,每行一个整数表示满足要求的数对(x,y)的个数
Sample Input
2 5 1 5 1
1 5 1 5 2
Sample Output
3
HINT
100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
题解:
首先先放个PoPoQQQ的ppt,其次再来份题解。(题解中的容斥定理把c和b写反了,无视就好)
总结一下,莫比乌斯反演有两种形式
形式一:
形式二:
本题用到的是第二种形式,题目要求求x∈[a,b],y∈[c,d]。x和y满足gcd(x,y)==k的x和y的数对数。
令f(i)为x∈[1,N],y∈[1,M],gcd(x,y)==i 的(x,y)数对数。
令F(i)为x∈[1,N],y∈[1,M],i|gcd(x,y) 的(x,y)数对数。
可以感觉,a和c的限制有些不好处理,不妨先忘掉a,c的限制。再看形式二,考虑f(i)与F(i)的关系,f(i)是gcd(x,y)==i,F(i)是gcd(x,y)==t*i(t∈Z),所以F(i)=sigma(f(d))(i|d),所以f(i)与F(i)正好满足形式二的第一个,可以利用莫比乌斯反演用F(i)表示f(i)。而F[i]=[N/i]*[M/i] ([]表示向下取整)
再考虑a和c的限制,设g(x,y,k)表示[1,x],[1,y] gcd(x1,y1)==k的对数,这个式子其实等价于g(x/k,y/k,1)。
再有容斥原理可得:Ans=g(b/k,d/k,1)-g((a-1)/k,d/k,1)-g(c/k,b/k,1)+g((a-1)/k,(c-1)/k,1),对于每个g(,,)我们都可以用f()来求。
O(n)的复杂度不足以解决此题,还有一个O(sqrt(n))的优化,具体看ppt或题解,讲的很清楚。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #include<vector> 8 #include<queue> 9 using namespace std; 10 typedef long long LL; 11 const LL maxn=50000; 12 LL mu[maxn],prime[maxn],sum[maxn],tot,N; 13 bool not_prime[maxn]; 14 LL a,b,c,d,k,ANS; 15 inline void pre(){ 16 memset(not_prime,false,sizeof(not_prime)); 17 mu[1]=1; 18 for(LL i=2;i<=maxn;i++){ 19 if(not_prime[i]==false){ 20 prime[++tot]=i; 21 mu[i]=-1; 22 } 23 for(LL j=1;j<=tot;j++){ 24 if(i*prime[j]>maxn) break; 25 not_prime[prime[j]*i]=true; 26 if(i%prime[j]==0){ 27 mu[prime[j]*i]=0; 28 break; 29 } 30 else mu[prime[j]*i]=-mu[i]; 31 } 32 } 33 } 34 inline LL solve(LL N,LL M){ 35 LL ans=0; 36 if(N>M) swap(N,M); 37 for(LL i=1,la=0;i<=N;i=la+1){ 38 la=min(N/(N/i),M/(M/i)); 39 ans+=(sum[la]-sum[i-1])*(N/i)*(M/i); 40 } 41 return ans; 42 } 43 int main(){ 44 // freopen("b.in","r",stdin); 45 // freopen("b.out","w",stdout); 46 pre(); 47 sum[0]=0; 48 for(LL i=1;i<=maxn;i++){ 49 sum[i]=sum[i-1]+mu[i]; 50 } 51 scanf("%d",&N); 52 while(N--){ 53 scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k); 54 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); 55 printf("%lld\n",ANS); 56 } 57 return 0; 58 }