1. 程式人生 > >[HAOI2011]Problem b

[HAOI2011]Problem b

一個 -- min memset type sum 遍歷 sizeof 最大公約數

題目描述

對於給出的n個詢問,每次求有多少個數對(x,y),滿足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函數為x和y的最大公約數。

輸入輸出格式

輸入格式:

第一行一個整數n,接下來n行每行五個整數,分別表示a、b、c、d、k

輸出格式:

共n行,每行一個整數表示滿足要求的數對(x,y)的個數

輸入輸出樣例

輸入樣例#1:
2
2 5 1 5 1
1 5 1 5 2
輸出樣例#1:
14
3

說明

100%的數據滿足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

題解:莫比烏斯反演+容斥原理+分塊

技術分享

技術分享 技術分享

a‘=a/k b‘=b/k

技術分享

技術分享

又根據容斥原理,算出ans1=cal(b/k,d/k),ans2=cal((a-1)/k,d/k),ans3=cal(b/k,(c-1)/k),ans4=cal((a-1)/k,(c-1)/k)

輸出的答案就是ans1-ans2-ans3+ans4

可以O(n)時間求解,但總時間復雜度為O(n^2)

似乎無路可走了,但這時出現了一種奇妙的方法,把復雜度降到了O(n√n)

根據上面,答案可以遍歷1~min(a‘,b‘)求解,但可以發現,一定範圍內的[a‘/d]是相同的,相同的值共有√n種

處理出μ(d)的前綴和,假設i~pos範圍內相同,則有s+=(sum[pos]-sum[i-1])*(a‘/i)*(b‘/i)

pos可以這麽求:pos=min(a‘/(a‘/i),b‘/(b‘/i)),想一想,看是不是這樣

 1 #include<iostream>
 2 #include<cstdio>
 3
#include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 bool vis[50001]; 9 int mu[50001],prime[50001]; 10 lol sum[50001]; 11 void get_mobious() 12 {int i,j; 13 memset(vis,0,sizeof(vis)); 14 mu[1]=1; 15 int tot=0; 16 for (i=2
;i<=50000;i++) 17 { 18 if (vis[i]==0) 19 { 20 prime[++tot]=i; 21 mu[i]=-1; 22 } 23 for (j=1;j<=tot;j++) 24 { 25 if (i*prime[j]>50000) break; 26 vis[i*prime[j]]=1; 27 if (i%prime[j]==0) 28 { 29 mu[i*prime[j]]=0; 30 break; 31 } 32 else mu[i*prime[j]]=-mu[i]; 33 } 34 } 35 sum[0]=0; 36 for (i=1;i<=50000;i++) 37 sum[i]=sum[i-1]+mu[i]; 38 } 39 lol cal(int x,int y) 40 {int r,i,pos; 41 lol s=0; 42 r=min(x,y); 43 for (i=1;i<=r;i=pos+1) 44 { 45 pos=min(x/(x/i),y/(y/i)); 46 s+=(sum[pos]-sum[i-1])*(x/i)*(y/i); 47 } 48 return s; 49 } 50 int main() 51 {int n,a,b,c,d,k; 52 cin>>n; 53 get_mobious(); 54 while (n--) 55 { 56 scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); 57 lol ans1=cal(b/k,d/k); 58 lol ans2=cal((a-1)/k,d/k); 59 lol ans3=cal(b/k,(c-1)/k); 60 lol ans4=cal((a-1)/k,(c-1)/k); 61 printf("%lld\n",ans1-ans2-ans3+ans4); 62 } 63 }

[HAOI2011]Problem b