[CQOI2015]选数
题目
正解
注意到如果我们限制的不是 \(\gcd\) 一定是 \(k\),而是 \(k\) 的倍数,那么这个题就特别好做了,即我们可以将 \(k\) 提出来,剩下的数在 \([\udiv{L}{k},\ddiv{H}{k}]\) 之间任意选数,令 \(S=\udiv{L}{k},W=\ddiv Hk\),那么答案就是 \((W-S+1)^n\).
这启示我们可以定义 \(f(k)\) 表示从 \([S,W]\) 中选出来的 \(n\) 个数的 \(\gcd\) 恰好为 \(d\) 的方案数,\(g(k)\) 表示从 \([S,W]\) 中选出来的 \(n\) 个数的 \(\gcd\) 为 \(d\) 的倍数的方案数,那么存在
令 \(d=1\),得到
后面
代码
const int maxn=2e6;
const int mod=1e9+7;
int mu[maxn+5];
int prime[maxn>>2],pcnt;
int sie[maxn+5];
inline void sieve(){
mu[1]=1;
rep(i,2,maxn){
if(!sie[i])prime[++pcnt]=i,mu[i]=mod-1;
for(int j=1;j<=pcnt && i*prime[j]<=maxn;++j){
sie[i*prime[j]]=1;
if(i%prime[j]==0){mu[i*prime[j]]=0;break;}
mu[i*prime[j]]=mod-mu[i];
}
}
// rep(i,1,10)printf("mu[%d] == %d\n",i,mu[i]);
rep(i,2,maxn)mu[i]=(mu[i]+mu[i-1])%mod;
}
int n,k,A,B;
inline int qkpow(int a,int n){
int ret=1;
for(;n>0;n>>=1,a=1ll*a*a%mod)if(n&1)
ret=1ll*ret*a%mod;
return ret;
}
map<int,int>mmu;
int S(const int n){
if(n<=maxn)return mu[n];
if(mmu[n])return mmu[n];
int ret=1;
for(int l=2,r;l<=n;l=r+1){
r=n/(n/l);
ret=(ret+mod-1ll*(r-l+1)*S(n/l)%mod)%mod;
}
return mmu[n]=ret;
}
inline int S(const int l,const int r){
int a=S(l-1),b=S(r);
return (b+mod-a)%mod;
}
signed main(){
sieve();
n=readin(1),k=readin(1),A=readin(1),B=readin(1);
A=((A-1)/k)+1,B/=k;--A;
int ans=0;
for(int l=1,r;l<=A;l=r+1){
r=Min(B/(B/l),A/(A/l));
int tmp=qkpow(B/l-A/l,n);
ans=(ans+1ll*S(l,r)*tmp%mod)%mod;
}
for(int l=A+1,r;l<=B;l=r+1){
r=B/(B/l);
int tmp=qkpow(B/l,n);
ans=(ans+1ll*S(l,r)*tmp%mod)%mod;
}
writc(ans,'\n');
return 0;
}
我们会发现还有一个条件我们没用上:\(H-L\le 10^5\). 这提示我们此路不通......
于是我们更换一下做法,目的还是一样的,我们要从 \([S,W]\) 中选出 \(n\) 个互质的数,先注意到下面这个性质:
在这个区间中选取不完全相同的 \(n\) 个数,这 \(n\) 个数的 \(\gcd\) 一定小于等于他们的极差。
证明很好证,不过要意识到却比较难。反证法即可。
然后,设 \(dp(i)\) 表示在区间 \([S,W]\) 选择 \(n\) 个数,他们的 \(\gcd\) 为 \(i\) 的方案数,那么就有:
后面的容斥要求了我们需要从大往小进行 \(dp\),注意特殊情况是 \(S=1\) 时,所有数都可以选择 \(1\). 复杂度带上一个调和级数,最后的复杂度为 \(\mathcal O((H-L)\ln n)\).
/** @author Arextre */
#include <bits/stdc++.h>
using namespace std;
#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#include <cassert>
namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
template<class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }
#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif
template<class T> inline T readret(T x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T> inline void readin(T& x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
if (x < 0) putchar('-'), x = -x;
static int __stk[55], __bit = 0;
do __stk[++__bit] = x % 10, x /= 10; while (x);
while (__bit) putchar(__stk[__bit--] ^ 48);
putchar(c);
}
} // namespace Elaina
using namespace Elaina;
const int mod = 1e9 + 7;
const int maxn = 1e5;
inline int qkpow(int a, int n) {
int ret = 1;
for (; n; n >>= 1, a = 1ll * a * a % mod)
if (n & 1) ret = 1ll * ret * a % mod;
return ret;
}
inline void chkAdd(int& x, const int& delta) { if ((x += delta) >= mod) x -= mod; }
inline void chkSub(int& x, const int& delta) { if ((x -= delta) < 0) x += mod; }
inline int plus(int x, int y) { return chkAdd(x, y), x; }
inline int subs(int x, int y) { return chkSub(x, y), x; }
int n, k, L, H, s, w;
inline void input() {
readin(n, k, L, H);
s = (L + k - 1) / k, w = H / k;
}
int dp[maxn + 5];
inline void solve() {
drep (d, w - s + 1, 1) {
dp[d] = subs(qkpow((w / d) - ((s - 1) / d), n), (w / d) - ((s - 1) / d));
for (int j = (d << 1); j <= w - s + 1; j += d) chkSub(dp[d], dp[j]);
}
if (s == 1) chkAdd(dp[1], 1);
writln(dp[1]);
}
signed main() {
input();
solve();
return 0;
}
用到の一些 \(\tt trick\)
首先是使用杜教筛处理 \(\sum\mu(i)\);
其次,最重要的就是遇到 \( f(k)=\sum_{k|d}\mu({d\over k})F(d)\) 而无法继续化简时,我们可以考虑对 \(F\) 的运算动一些手脚,让 \(F'({d\over k})=F(d)\),然后再枚举 \(d\) 是 \(k\) 的几倍,进行分块运算.