1. 程式人生 > >JZYZOJ1518 [haoi2011]b 莫比烏斯反演 分塊 容斥

JZYZOJ1518 [haoi2011]b 莫比烏斯反演 分塊 容斥

namespace tar ios closed 速度 esp 計算 i++ pac

http://172.20.6.3/Problem_Show.asp?id=1518
最開始只想到了n^2的寫法,肯定要超時的,所以要對求gcd的過程進行優化。
首先是前綴和容斥,很好理解。
第二個優化大致如下:
u為莫比烏斯函數,t為gcd(x,y)為i的倍數的數的個數;
滿足gcd(x,y)=1的數字對的數量=sigma(1<=i<=min(x,y))u[i]*t[i];
t[i]=(x/i)*(y-i);
由小數向下取整可知有連續的i滿足x/i為定值,y/i也是定值,所以可以分塊計算,用u[i]的前綴和*定值,加快求gcd(x,y)=1的對數的速度。

代碼

技術分享
 1 #include<cstdio>
 2
#include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<iostream> 6 using namespace std; 7 const int maxn=50010; 8 int n; 9 int a,b,c,d,k; 10 bool vis[maxn]={}; 11 int ur[maxn]={},su[maxn]={},sum[maxn]={},tot=0; 12 void doit(){ 13 sum[1]=1;ur[1]=1; 14 for
(int i=2;i<maxn;i++){ 15 if(!vis[i]){ur[i]=-1;su[++tot]=i;} 16 for(int j=1;j<=tot&&i*su[j]<maxn;j++){ 17 int z=i*su[j];vis[z]=1; 18 if(i%su[j]==0)break; 19 ur[z]=ur[su[j]]*ur[i]; 20 } 21 sum[i]=sum[i-1]+ur[i]; 22 }
23 } 24 int getit(int x,int y){ 25 int z=0,nex=0; 26 if(x>y)swap(x,y); 27 for(int i=1;i<=x;i=nex+1){ 28 int xx=x/i,yy=y/i; 29 nex=min(x/xx,y/yy); 30 z+=(sum[nex]-sum[i-1])*xx*yy; 31 } 32 return z; 33 } 34 int main(){ 35 doit();int ans=0; 36 scanf("%d",&n); 37 while(n-->0){ 38 scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); 39 a-=1;c-=1; 40 a/=k;b/=k;c/=k;d/=k; 41 ans=0;ans+=getit(b,d);ans-=getit(b,c);ans-=getit(a,d);ans+=getit(a,c); 42 printf("%d\n",ans); 43 } 44 return 0; 45 }
View Code

JZYZOJ1518 [haoi2011]b 莫比烏斯反演 分塊 容斥