杜教筛与莫比乌斯反演

首先是杜教筛套路式子

其中n很大一般 左右导致常规线性筛超时

一般做法:

再找一和卷积起来有特殊性质的函数

然后就发现右边等式最后一项除以是要求的答案

这玩意可以递归下去

一般会在我们的选择下比较好算


先是一道模板题

BZOJ3944

Description

给定一个正整数N(N<=)求

Input

一共行 第行为数据组数行每行一个非负整数N,代表一组询问

Output

一共T行,每行两个用空格分隔的数ans1,ans2

对于这道题可以知道 具体实现我就使用map了=.=

Code

1
2
3
4
5
6
7
8
9
10
inline ll solve(int opt,unsigned x){
if (x<MAXN) return opt?sum1[x]:sum0[x];
if (M[opt][x]) return M[opt][x];
ll ret=opt?1ll:1ll*x*(x+1)>>1;
for (unsigned i=2,j=2;i<=x;i=j+1){
j=x/(x/i);
ret-=solve(opt,x/i)*(j-i+1);
}
return M[opt][x]=ret;
}

傻逼题讲完了T.T


BZOJ3930

Description

我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。 ### Input 输入一行,包含4个空格分开的正整数,依次为N,K,L和H。 ### Output 输出一个整数,为所求方案数。

Sample Input

1
2 2 2 4

Sample Output

1
3

HINT

样例解释 所有可能的选择方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4) 其中最大公约数等于2的只有3组:(2, 2), (2, 4), (4, 2) 对于100%的数据,

一看这数据范围就懵逼了=.=

就变成求区间的方案了

然后看上去要杜教筛不过可以枚举选的N个数的

一堆差不超过的数的也在以内(易证)

怎么求?

区间任选个数使得都是一个数的倍数=.=快速幂搞搞

并且选的这个数不能全部一样

所以选的方案要减去这个区间的数的个数(刚好这么多种所有数都一样的方案)

还有个特判为区间是否包含1,包含时的

Code

1
2
3
4
5
6
7
8
9
10
11
12
inline int getf(int x){
int lt=(L%x)?L/x+1:L/x,rt=R/x;
return add(qpow(rt-lt+1,N),-rt+lt-1);
}
int main(){
pre();
read(N);read(K);read(L);read(R);
L=L%K?L/K+1:L/K;R/=K;n=R-L;
for (int i=1;i<=n;i++)
ans=add(ans,mul(mu[i],getf(i)));
if (L<=1 && 1<=R) ans=add(ans,1);
}

BZOJ3529

Description

有一张 的数表,其第 行第的数值为 能同时整除 的所有自然数之和。给定 , 计算数表中不大于 的数之和。 ### Input 输入包含多组数据。 输入的第一行一个整数表示测试点内的数据组数 接下来行,每行三个整数描述一组数据。 ### Output 对每组数据,输出一行一个整数,表示答案模的值。

Sample Input

1
2
3
2
4 4 3
10 10 5

Sample Output

1
2
20
148

好像很难搞=.=不过1e5数据范围可以不用杜教筛 吼啊

写成数学符号就是

啥玩意没听过==

先考虑简化版问题:不考虑a的影响怎么做

首先约数和可以线性筛预处理:

假设是个质数 非常好然后我们可以

所以答案就是 就可以用根号分块和预处理前缀和水了

然后考虑怎么加入a的限制

emmm

N,M才,可以按排序再将a排序,动态维护下前缀和就好了

动态维护前缀和的数据结构比如说树状数组

可以自然溢出取与

然后这题就结束了

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
inline void pre(){
mu[1]=1;t[1]=1;F[1].xx=1;
for (int i=2;i<=MAXN;i++){
if (!vis[i]) prime[++cnt]=i,vis[i]=i,F[i].xx=i+1,t[i]=i+1,mu[i]=-1;
for (int j=1;j<=cnt && i*prime[j]<=MAXN;j++){
vis[i*prime[j]]=1;
if (i%prime[j]) {
mu[i*prime[j]]=-mu[i];
F[i*prime[j]].xx=F[i].xx*1+prime[j]),t[i*prime[j]]=prime[j]+1;
}else {
mu[i*prime[j]]=0;
t[i*prime[j]]=t[i]*prime[j]+1;
F[i*prime[j]].xx=F[i].xx/t[i]*t[i*prime[j]];
break;
}
}
}
for (int i=1;i<=MAXN;i++) F[i].yy=i;
}
inline void fadd(int x){
for (int i=1;i*F[x].yy<=MAXN;i++)
add(i*F[x].yy,F[x].xx*mu[i]);
}
//这里省略
sort(q+1,q+N+1); sort(F+1,F+MAXN+1);
for (int i=1,j=0;i<=N;i++){
while (j<MAXN&&F[j+1].xx<=q[i].a) fadd(++j);
n=q[i].n;m=q[i].m;
if (n>m) swap(n,m);
for (int l=1,r=1;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
ans[q[i].id]+=(n/l)*(m/l)*(query(r)-query(l-1));
}
}

BZOJ4652

Description

牛牛是一个热爱算法设计的高中生。在他设计的算法中,常常会使用带小数的数进行计算。牛牛认为,如果在 进制下,一个数的小数部分是纯循环的,那么它就是美的。现在,牛牛想知道:对于已知的十进制数 ,在 k 进制下,有多少个数值上互不相等的纯循环小数,可以用分数 表示,其中 ,且 是整数。一个数是纯循环的,当且仅当其可以写成以下形式:其中, 是一个整数,;对于 进制下的一位数字。例如,在十进制下,是纯循环的,它可以用 等分数表示;在十进制下,则不是纯循环的,它可以用 等分数表示。需要特别注意的是,我们认为一个整数是纯循环的,因为它的小数部分可以表示成 的循环或是 的循环;而一个小数部分非 的有限小数不是纯循环的。 ### Input 只有一行,包含三个十进制数N,M,K意义如题所述,保证 ### Output 一行一个整数,表示满足条件的美的数的个数。

Sample Input

1
2 6 10

Sample Output

1
4

HINT

满足条件的数分别是: 1/1=1.0000…… 1/3=0.3333…… 2/1=2.0000…… 2/3=0.6666…… 1/1 和 2/2 虽然都是纯循环小数,但因为它们相等,因此只计数一次;同样,1/3 和 2/6 也只计数一次。

啥,纯循环小数,没听过0.0

首先这题很显然可以看出是要求一些分子分母互质的数的个数(不互质的会重复出现)

先不管那么多式子写起来

还有一个可能是对的不会证得就是对于一个纯循环小数 如果 仍与分母b互质(不然就被约分消掉了),那么应该仍是个纯循环小数(可能是错的我也不会证),然后就说明了是否为纯循环小数在分子分母互质的情况下与分子关系不大(蛤也不敢说是不是完全没有关系)

还是以为分子在进制下找找规律打表

🐸🐸🐸 将符合要求的分母写下来嗯没有偶数,但也跟质数不像,然后通过严谨而又细致的猜测推理我们发现这东西应该是要求分母与k互质

具体为什么=.=

证明From 神仙网友

我们先来回想一下,我们是怎样使用除法来判断一个分数 是否是纯循环小数的。显然我们是一路除下去,什么时候出现了相同的余数,那么这个数就是一个循环小数。如果第一个重复的余数是,那么这个数就是纯循环小数。这种方法实际上就是存在一个数,使得:

又由于题目要求值不重复,那么我们可以得到。所以,我们可以推出。所以我们只需即可。

所以事情变成了我们熟悉的样子

然后开始乱搞 好啊然后发现这个东西怎么看怎么不好算,长得很像但又多了个所以不能直接合并

所以开始打暴力可能需要分别计算 对于上面那个东西由上上道题类似的想法发现 如果那么所有的都与互质

所以只需预处理的数中每个数与k互质的数的个数的前缀和(特别注意这玩意不是, 因为表示小于等于的数中与互质的数个数而不是与互质的数的个数)乘上,对于剩下的不满的再单独计算即可。代码其实就一句int f(int x){return (x/K)*F[K]+F[x%K];}

这么简单搞定了,那么能否同理呢?

如果是这样这题就不会是最后一道了

很简单的例子比如那么不一定等于

于是开始硬刚 然后发现当对答案没有贡献,所以考虑 突然发现这式子似曾相识。。。

!!!

所以得到 对于k=1时找到最开始的定义 是我们最开始讲的第一道=.=然后就完了

然后又变成简单递归题了,复杂度

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
map<pii,int>MA;
inline int f(int x){return (x/K)*F[K]+F[x%K];}
inline int g(int x,int k){//比一般杜教筛多了个参数0.0
if ((k==1 && x<MAXN)|| !x) return sum[x];
if (MA[pii(x,k)]) return MA[pii(x,k)];
int ret=0;
if (k==1){
ret++;
for (int i=2,j=2;i<=x;i=j+1){
j=x/(x/i);
ret-=(j-i+1)*g(x/i,1);
}
}else{
for (int i=1;i*i<=k;i++) if (!(k%i)){//这里也只用枚举到sqrt(k),因为因数是成对的
if (mu[i])ret+=g(x/i,i); //这里防止mu[i]等于0白算一堆
if (mu[k/i] && (i*i^k)) ret+=g(x/(k/i),k/i);//i*i!=k才再算不然贡献算两遍
}
}
return MA[pii(x,k)]=ret;
}
//这里还是省略防止ctrl+c
pre();
n=min(N,M);
for (int i=1,j=1;i<=n;i=j+1){
j=min(N/(N/i),M/(M/i));
cur=S(j,K);
ans+=(ll)(cur-lst)*f(M/i)*(N/i);
lst=cur;
}

总结

杜教筛是看上去很难实际上也没那么难的东西,希望今天大家都听懂了=.=