BZOJ 3930 [CQOI2015]选数
Solution
题目要求:
设要求的答案为
f(K)
f
(
K
)
,表示
考虑构造一个函数 F(K) F ( K ) ,表示
那么
f,F
f
,
F
之间有如下关系
F(n)=∑Rn|df(d)
F
(
n
)
=
∑
n
|
d
R
f
(
d
)
进行莫比乌斯反演
得到
f(n)=∑Rn|dμ(dn)F(d)
f
(
n
)
=
∑
n
|
d
R
μ
(
d
n
)
F
(
d
)
F(n)=(⌊Rn⌋−⌊L−1n⌋)N F ( n ) = ( ⌊ R n ⌋ − ⌊ L − 1 n ⌋ ) N
所以原式化为:
设
T=dK
T
=
d
K
,枚举
T
T
,得:
用一个分块+前缀和搞定
看上去好像做完了的样子
但是若
K=1,R=1e9
K
=
1
,
R
=
1
e
9
就会神奇的发现
前缀和爆了
显然只能开到
1e7
1
e
7
(某些奇怪的OJ只有128MB内存)
但是要求你求到
1e9
1
e
9
所以说需要一些神奇的方法(玄学到自己都不清不白)
因为
∑d|iμ(d)=[i==1]
∑
d
|
i
μ
(
d
)
=
[
i
==
1
]
可得
枚举 d d (约数)可得
等价于
因为 i,j i , j 等价,所以
等价于
等价于枚举 d d (次数)
设
那么
这样我们就可以递归的将 sum(n) s u m ( n ) 求出
而且将前 1e6 1 e 6 的 sum s u m 打表出来,直接代入,可极大加速
详细代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 1000000010;
const int N = 1000005;
const int mod = 1000000007;
unordered_map <int,long long> mu_sum;
int n,k,l,r;
ll pri[N],tot,mu[N];
ll s[N];
bool mark[N];
void get() {
mu[1]=1;
for(int i=2;i<=1000000;++i) {
if(!mark[i]) {
pri[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot && i*pri[j]<=1000000;++j) {
mark[i*pri[j]]=1;
if(i%pri[j]==0) {break;}
mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=1000000;++i) {
mu[i]+=mu[i-1];
s[i]=s[i-1]+mu[i];
}
}
ll qpow(ll a,ll b) {
ll ans=1;
while(b) {
if(b&1) {
ans*=a;
ans%=mod;
}
b>>=1;
a*=a;
a%=mod;
}
return ans;
}
ll get_mu(int x) {
if(x<=1000000) return mu[x];
if(mu_sum.find(x)!=mu_sum.end())
return mu_sum[x];
int pos;
ll ans=1;
for(int i=1;i<=x;i=pos+1) {
pos=x/(x/i);
if(x/i-1) ans-=(get_mu(pos)-get_mu(i-1))*(x/i-1);
}
return mu_sum[x]=ans;
}
int main() {
scanf("%d%d%d%d",&n,&k,&l,&r);
r/=k;l=(l-1)/k;
get();
ll ans=0;
int pos=0;
for(int i=1;i<=r;i=pos+1) {
pos=min(r/(r/i),l/i?l/(l/i):INF);
ans+=(get_mu(pos)-get_mu(i-1))*qpow((r/i)-(l/i),n);
ans%=mod;
}
printf("%lld\n",(ans+mod)%mod);
return 0;
}