【题解】洛谷P3172 [CQOI2015] 选数[杜教筛]
题面:从 选
个数(可能重复),一共有
种取法。问这些取法中满足
个数最大公约数为
的有多少种。
第一眼就知道要把 ,
。
然后问题就变成求满足 个数最大公约数为
有多少种。
然后就莫反+杜教筛,但还有递推做法,我都讲下。
(1)莫反+杜教筛做法
分两种推公式的方法,一种是造俩函数然后倍数莫反,我觉得不常用且不好想就没写在这里。
想看可以去这里。
洛谷上LaTeX用的都挺感人的,我换种人能看的懂的方式。
题面:从 选
个数(可能重复),一共有
种取法。问这些取法中满足
个数最大公约数为
的有多少种。
第一眼就知道要把 ,
。
然后问题就变成求满足 个数最大公约数为
有多少种。
公式:
没见过这么丑的,硬着头皮反演吧。
注意这个 ,直接除以
的话,有些情况会不符合我们想要的“
中所有的
的倍数”。
考虑让 ,实际上
。
同学们自己试下 和
的情况就知道为什么了。
代码:
#include<bits/stdc++.h>
using namespace std;
template<typename T> void qread(T &x){x=0; int f=1; char c=getchar();for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;for(; isdigit(c); c=getchar()) x=x*10+(c-'0');x*=f;
}
typedef long long LL;
const int N=5e6+10;
const LL P=1e9+7;
int pr, prime[N];
LL mu[N];
bool v[N];
void init(){pr=0; memset(v, 0, sizeof(v));mu[1]=1; mu[0]=0;for(int i=2; i<=N-10; i++){if(!v[i]){pr++; prime[pr]=i;mu[i]=-1;} for(int j=1; (j<=pr) && (i*prime[j]<=N-10); j++){int p=prime[j];v[i*p]=1;if(i%p==0){mu[i*p]=0;break;}else mu[i*p]=-mu[i]; }}for(int i=1; i<=N-10; i++) mu[i]+=mu[i-1];
}
LL q_pow(LL a, LL b){LL res=1;while(b){if(b&1) res=res*a%P;a=a*a%P; b/=2;}return res;
}
LL n, K, L, H;
map<LL, LL> hsm;
LL calc(LL x){if(x<=N-10) return mu[x];if(hsm.find(x)!=hsm.end()) return hsm[x];LL res=1;for(int i=2, j; i<=x; i=j+1){j=x/(x/i);res=(res-calc(x/i)*(j-i+1)%P+P)%P;}return hsm[x]=res;
}
LL solve(){LL res=0; L--;for(int i=1, j; i<=H; i=j+1){if(!(L/i)) j=H/(H/i); //到了一定位置大家的 L/i都变成 0,没有参考价值了 else j=min(L/(L/i), H/(H/i));res=(res+q_pow(H/i-L/i, n)*((calc(j)-calc(i-1))%P+P)%P)%P;}return res;
}
int main(){init();qread(n); qread(K); qread(L); qread(H);H=H/K; L=((L%K==0)? L=L/K: L=L/K+1);printf("%lld\n", solve());return 0;
}
(2)递推做法
其实差不多,一样的容斥,本来应该超时的,不过本题 比较小可以钻空子。
(我个人不喜欢这个做法,就不讲了,可以去洛谷看题解)