[题解] BZOJ 3930 [CQOI2015]选数

BZOJ 3930 [CQOI2015]选数

Solution

题目要求:

a 1 = L R a 2 = L R a N = L R [ g c d ( a 1 , a 2 , , a N ) = K ]

设要求的答案为 f ( K ) ,表示

a 1 = L R a 2 = L R a N = L R [ g c d ( a 1 , a 2 , , a N ) = K ]

考虑构造一个函数 F ( K ) ,表示
a 1 = L R a 2 = L R a N = L R [ K | g c d ( a 1 , a 2 , , a N ) ]

那么 f , F 之间有如下关系
F ( n ) = n | d R f ( d )

进行莫比乌斯反演
得到 f ( n ) = n | d R μ ( d n ) F ( d )

F ( n ) = ( R n L 1 n ) N

所以原式化为:

f ( K ) = K | d R μ ( d K ) ( R d L 1 d ) N

T = d K ,枚举 T ,得:

f ( K ) = T = 1 R K μ ( T ) ( R K T L 1 K T ) N

用一个分块+前缀和搞定
看上去好像做完了的样子
但是若 K = 1 , R = 1 e 9 就会神奇的发现
前缀和爆了
显然只能开到 1 e 7 (某些奇怪的OJ只有128MB内存)
但是要求你求到 1 e 9

所以说需要一些神奇的方法(玄学到自己都不清不白)
因为 d | i μ ( d ) = [ i == 1 ]
可得

i = 1 n d | i μ ( d ) = 1

枚举 d (约数)可得
d = 1 n i = 1 n d μ ( d ) = 1

等价于
i = 1 , d = 1 i d <= n d μ ( i )

因为 i , j 等价,所以
i = 1 , d = 1 i d <= n i μ ( d )

等价于
d = 1 n i = 1 n d μ ( i ) = 1

等价于枚举 d (次数)
d = 1 n i = 1 n d μ ( i ) = 1


s u m ( n ) = i = 1 n μ ( i )

那么

d = 1 n s u m ( n d ) = 1

s u m ( n ) = 1 d = 2 n s u m ( n d )

这样我们就可以递归的将 s u m ( n ) 求出
而且将前 1 e 6 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;
}

猜你喜欢

转载自blog.csdn.net/force_chl/article/details/80642004
今日推荐