题目链接:选数
MDZZ,这种SB题我都Wa了这么多发,彻底没救系列~
首先,我们可以发现1,如果我们选了两个不同的数,那么它们的\(\gcd\)不会超过\(r-l+1\)。于是,我们可以设一个\(f_i\)表示任取\(n\)个数,它们的\(\gcd\)为\(ik\)的方案数,最后我们要的答案就是\(f_1\)。我们考虑容斥一下,在求\(f_i\)的时候,先把\([l,r]\)中是\(ik\)倍数的数全部拿出来,然后任意选\(n\)个,这样选出来的数他们的\(\gcd\)一定是\(ik\)的倍数。于是,我们只需减去\(f_{2i},f_{3i},\dots,f_{\lfloor \frac{r-l+1}{i}\rfloor i}\)即可。
这样的话,有可能还有些数\(\gcd\)是\(ik\)的倍数我们却没统计到。由于这些未统计的\(\gcd\)肯定比\(r-l+1\)大,那么肯定是选了\(n\)个相同的数,于是这一部分就可以直接算了。
下面贴代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define maxn 100010 #define mod 1000000007 using namespace std; typedef long long llg; int n,k,l,r; llg f[maxn]; void gi(llg &x){if(x>=mod) x%=mod;} llg mi(llg a,int b){ llg s=1; while(b){ if(b&1) s=s*a,gi(s); a=a*a,gi(a); b>>=1; } return s; } int main(){ File("a"); scanf("%d %d %d %d",&n,&k,&l,&r); int rr=min(r/k,r-l+1); for(int i=rr,x,y=rr*k,z;i;i--,y-=k){ x=(r/y)-(l/y); if(l%y==0) x++; z=(y>=l); for(int j=i<<1;j<=rr;j+=i) f[i]-=f[j],(f[i]+=mod)%=mod,z+=(j*k>=l); f[i]+=mi(x,n)-x+z; (f[i]+=mod)%=mod; } printf("%lld",(f[1]+mod)%mod); return 0; }
时间: 2024-10-14 13:47:25