莫(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

 

转载于:https://www.cnblogs.com/Blue233333/p/8157770.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值