今天B题的题解
配合董神讲解食用更佳
先分析一下题目给出的公式吧
根据定义:一个正整数的正约数,是每个质因数的幂次加一之后乘起来。
Q1:如何理解这个公式呢?
首先明确正整数的正约数的质因数是正整数的一部分。所以我们的正约数就是从正整数的质因数中随便选几个数乘起来
例如: 30 = 2 ∗ 3 ∗ 5 30=2*3*5 30=2∗3∗5
那么对于每一个质因数我们有选和不选两种选择。那么质因数个数就有 ( 1 + 1 ) ∗ ( 1 + 1 ) ∗ ( 1 + 1 ) = 8 (1+1)*(1+1)*(1+1)=8 (1+1)∗(1+1)∗(1+1)=8个
Q2:如果同类质因数有多个呢?
例如: 60 = 2 2 ∗ 3 ∗ 5 60=2^2*3*5 60=22∗3∗5
不难看出,对于质因数2有3种选择方案,选一个,选两个,不选;排除掉不选的情况,你会发现幂次恰好是我们的选择这个质因数方案数。
公式理解清楚了之后,我们来想想看如何保证我们的正约数的所有质因数不超过P。
根据上面对公式的分析,我们不难得出,对于比P小的数,选择方案是不变的,而对于比p大的数的选法实际上只有不选这一种(相当于乘1)。
例如:求60有多少个正约数所有质因数小于5
因为
60
=
2
2
∗
3
∗
5
60=2^2*3*5
60=22∗3∗5,小于5的质因数有2,3。那么答案就是
(
2
+
1
)
∗
(
1
+
1
)
∗
1
=
6
(2+1)*(1+1)*1=6
(2+1)∗(1+1)∗1=6
剩下的就是质因数分解了。但是我们发现这是许多
1
0
5
10^5
105 内的数乘积。完了,搞不了。但没有关系,实际影响到我们的只有他们的质因数而已。也就是我们讨论的数的规模不会超过
1
0
5
10^5
105 。
但是询问区间是跳跃着的啊,那没有问题。毕竟是离线询问,我们排个序就完事了。
乘积的维护我个人选择的是树状数组,跟维护前缀和的概念是差不多的。
但我处理的有点复杂emm(高端代码,是所望于群公)
void merge(int x,int k)
{
ll tmp=(sum[x]+k)%mod,ni=quick(sum[x],mod-2);//求逆元
//这里我单独用了个sum来记录x原先的值。
for (int i=x;i<=Max;i+=lowbit(i)) c[i]=c[i]*ni%mod*tmp%mod;
sum[x]=tmp;
}
ll Sum(int x)
{
ll ans=1;
for (int i=x;i>0;i-=lowbit(i)) ans=ans*c[i]%mod;
return ans;
}
到这里,如果你会因数分解的话,这道题就结束了。
但我觉得应该是许多人不会因数分解。我这里给出的是O(n)的欧拉筛分解(好东西)
对于一个数
i
i
i,我们设它其中一个质因数为
p
p
p 那么
i
i
i 的质因数分解情况就是在
i
/
p
i/p
i/p的分解情况上多了个p。
我们用欧拉筛先求出质数,以及求出每个数的质因数,然后从2讨论到n即可
(说的似乎有点模糊,我给一下代码片段可能会明晰一点。依旧模糊)
for (int i=2;i<=n;i++)
{
int k=i/p(p就是上面给出的质因数)
if (k==1) continue(跳过质数情况)
····//循环讨论
}