题解 P3172 【[CQOI2015]选数】

asuldb

2018-12-09 19:29:27

Solution

这道题自然是可以反演的 按照反演的套路我们先设出两个函数 $F(n)$表示从$[L,H]$中任选$N$个数的最大公约数是$n$或者$n$的倍数的情况数 $f(n)$表示从$[L,H]$中任选$N$个数的最大公约数是$n$的情况数 非常显然的是 $$F(n)=\sum_{n|d}f(d)$$ $$f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)$$ 开始反演了 首先我们发现我们求$f(k)$并不好求,因为没有办法整除分块 所一我们把$L/k,H/k$之后求$f(1)$就好了 吗? 显然并不行啊 我们考虑一下如果$L\%k!=0$,$\left \lfloor \frac{L}{k} \right \rfloor\times k<L$,就会使一些不在$[L,H]$内的数混进答案里了 所以如果$L\%k!=0$的话,除以$k$之后再将$L+1$ 之后就是如何表示$F$了 非常显然就是 $$F(n)=(\left \lfloor \frac{H}{n}\right \rfloor-\left \lfloor \frac{L-1}{n}\right \rfloor)^N$$ 了 $$f(1)=\sum_{i=1}\mu(i)F(i)$$ 把$F(i)$相等的用整除分块处理 但是这道题的$H$非常大,甚至都不能线筛 不能线筛杜教筛总可以了吧,于是就可以用$O(H^{\frac{2}{3}})$的复杂度解决这道题 ```cpp #include<iostream> #include<cstring> #include<cstdio> #include<tr1/unordered_map> #define re register #define maxn 5000001 #define LL long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) const int mod=1000000007; using namespace std::tr1; unordered_map<int,int> ma; int p[maxn>>1],f[maxn],mu[maxn]; inline LL quick(int a,int b) { LL S=1; while(b) {if(b&1) S=S*a%mod;b>>=1;a=(LL)a*(LL)a%mod;} return S; } int N,K,L,H,M; int solve(int x) { if(x<=M) return mu[x]; if(ma.find(x)!=ma.end()) return ma[x]; int ans=1; for(re int l=2,r;l<=x;l=r+1) { r=x/(x/l); ans-=solve(x/l)*(r-l+1); } return ma[x]=ans; } int main() { scanf("%d%d%d%d",&N,&K,&L,&H); H/=K; if(L%K==0) L=L/K; else L=L/K+1; M=min(H,5000000); mu[1]=f[1]=1; for(re int i=2;i<=M;i++) { if(!f[i]) p[++p[0]]=i,mu[i]=-1; for(re int j=1;j<=p[0]&&p[j]*i<=M;j++) { f[p[j]*i]=1; if(i%p[j]==0) break; mu[i*p[j]]=-1*mu[i]; } } for(re int i=1;i<=M;i++) mu[i]+=mu[i-1]; LL ans=0;L--; for(re int l=1,r;l<=H;l=r+1) { if(!(L/l)) r=H/(H/l); else r=min(H/(H/l),L/(L/l)); ans=(ans+quick(H/l-L/l,N)*(LL)(solve(r)-solve(l-1))%mod)%mod; } printf("%lld\n",((ans%mod)+mod)%mod); return 0; } ```