莫(meng)比(bi)烏斯反演--BZOJ2301: [HAOI2011]Problem b
n<=50000個詢問,每次問a<=x<=b,c<=y<=d中有多少gcd(x,y)=K的(x,y)。a,b,c,d,K<=50000。
這大概是入門題辣。。這裏記一波筆記
當難以計算f(i)而易於計算他的反演式g(i)時,可以通過計算g(i)->反演得到f(i)。
先放莫比烏斯函數的性質:$\sum_{d|i} \mu(d)=\left\{\begin{matrix} 1,i=1\\0,i>1\end{matrix}\right.$,$\sum_{d|i}(\mu(d)*n/d)=\varphi(i)$。
反演式一:$g(i)=\sum_{d|i} f(i) ------> f(i)=\sum_{d|i} \mu(d)g(i/d)$。
證明:$\sum_{d|i} \mu(d)g(i/d)=\sum_{d|i} \mu(d) \sum_{d_1|(i/d)} f(d_1)=\sum_{d|i} \sum_{d_1|(i/d)} \mu(d) f(d_1)$。
註意到$dd_1j=i$,所以對每一個$d=d_2$,$d_1=d_3$都有一個$d=d_3$,$d_1=d_2$與之對應。
所以$上式=\sum_{d|i} \sum{d_1|(i/d)} f(d) \mu(d_1)=\sum_{d|i} f(d) \sum{d_1|(i/d)} \mu(d_1)$,由莫比烏斯函數性質得$=f(i)$。
反演式二:$g(i)=\sum_{i|d} f(i) ------> f(i)=\sum_{i|d} \mu(d/i)g(d)$。
證明同上,略。
這題先容斥,變成問1<=x<=n,1<=y<=m中有多少(x,y)=K,由於(x,y)=K的充要條件是(x/K,y/K)=1,所以變成1<=x<=n/K,1<=y<=m/K中有多少(x,y)=1。
為什麽這麽變呢,因為假如題目求的是f(i),表示1<=x<=n,1<=y<=m中有多少(x,y)=i,那反演式g(i)表示1<=x<=n,1<=y<=m中有多少i|(x,y),g(i)和f(i)滿足反演式2。
而明顯的,$g(i)=(n/i)*(m/i)$,這裏/是向下取整,所以$f(i)=\sum_{i|d} \mu(d/i)g(d)=\sum_{i|d} \mu(d/i)(n/d)(m/d)$。
這時可以發現(n/d)和(m/d)分別只有$2\sqrt(n)$和$2\sqrt(m)$種取值,把他倆分別叫a和b,而隨著d增大a和b會緩慢增大,可能a增大b不變,b增大a不變,也可能a,b都增大,都不變。那麽數對((n/d),(m/d))最多只有$2\sqrt(n)+2\sqrt(m)$個,因此(n/d)*(m/d)最多只有$2\sqrt(n)+2\sqrt(m)$種取值。
如果上面的i=1,那麽只需要枚舉這$2\sqrt(n)+2\sqrt(m)$個(n/d)*(m/d)的值就可以在根號時間內算出答案,因為一個(n/d)*(m/d)的值對應一段連續的d,如果i=1,就可以把連續一段莫比烏斯函數以前綴和來O(1)求和。
入門題。
1 #include<cstring> 2 #include<cstdlib> 3 #include<cstdio> 4 //#include<assert.h> 5 #include<algorithm> 6 //#include<iostream> 7 using namespace std; 8 9 int a,b,c,d,K,T; 10 11 #define maxn 50011 12 int miu[maxn],prime[maxn],lp=0,summiu[maxn]; bool notprime[maxn]; 13 void pre(int n=50001) 14 { 15 miu[1]=summiu[1]=1; 16 for (int i=2;i<=n;i++) 17 { 18 if (!notprime[i]) {prime[++lp]=i; miu[i]=-1;} 19 summiu[i]=summiu[i-1]+miu[i]; 20 for (int j=1;j<=lp && 1ll*i*prime[j]<=n;j++) 21 { 22 notprime[i*prime[j]]=1; 23 if (i%prime[j]) miu[i*prime[j]]=-miu[i]; 24 else {miu[i*prime[j]]=0; break;} 25 } 26 } 27 } 28 29 #define LL long long 30 LL solve(int p,int q) 31 { 32 LL ans=0; 33 for (int i=1,last,to=min(p,q);i<=to;i=last+1) 34 { 35 last=min(p/(p/i),q/(q/i)); 36 ans+=1ll*(p/i)*(q/i)*(summiu[last]-summiu[i-1]); 37 } 38 return ans; 39 } 40 41 int main() 42 { 43 pre(); 44 scanf("%d",&T); 45 while (T--) 46 { 47 scanf("%d%d%d%d%d",&a,&b,&c,&d,&K); 48 printf("%lld\n",solve(b/K,d/K)-solve((a-1)/K,d/K)-solve(b/K,(c-1)/K)+solve((a-1)/K,(c-1)/K)); 49 } 50 return 0; 51 }View Code
莫(meng)比(bi)烏斯反演--BZOJ2301: [HAOI2011]Problem b