BZOJ3930 【CQOI2015】选数

感觉水水的一道题。
网上说什么mobius反演,但是感觉很麻烦,然后就感觉可以容斥乱搞,后来发现不用容斥。
如果我们用f[i]表示gcd刚好为k*i的选数方案,那么f[1]就是答案,然后我们就for i = 100000 to 1,从大往小算。
对于每一个i,先算在这个区间里面有多少个k*i的倍数,然后N次方之。
这就是gcd至少为k*i的方案数
然后减去f[i*j] j = 1,2,3….MAXJ。
i*j到100000就好了,因为这个区间最多只有100000长。
可能i*j在100000以上还会有需要减掉的,我们考虑计算这些数对答案的贡献,首先同一个gcd只会有1个数,这些gcd的个数就是在原区间里面除以K>100000且为i的倍数的数,不是很好求吗。。
然后就A了,跑得飞快,代码还短,内存还小。

不知道mobius反演是用来干什么的←_←

#include 
#include 
#include 
#include 
#include 
#define MOD 1000000007
#ifdef WIN32
#define LL "%I64d"
#else 
#define LL "%lld"
#endif
using namespace std;

long long qpow(long long x,int y) {
    if (y == 0) return 1;
    if (y == 1) return x;
    long long res = qpow(x,y / 2);
    res = (res * res) % MOD;
    res = (res * qpow(x, y % 2)) % MOD;
    return res;
}

int get_sum(int l,int r,long long k) {
    if (l > r) return 0;
    int ll = l / k;
    if (l % k) ll++;
    return r / k - ll + 1;
}
int n,k,l,r;
long long f[200010];
int main() {
    scanf("%d%d%d%d",&n,&k,&l,&r);
    int ll = l / k;
    if (ll % k) ll ++;
    ll = max(ll,100001);
    for (int i=100000;i>=1;i--) {
        long long cur = (long long)k * i;
        long long s = get_sum(l,r,cur);
        f[i] = qpow(s,n);
        long long ss = get_sum(ll,r / k,i);
        f[i] = (f[i] - ss + MOD) % MOD;
        for (int j=2*i;j<=100000;j+=i) {
            f[i] = (f[i] - f[j] + MOD) % MOD;
        }
    }
    printf(LL"\n",f[1]);
}


你可能感兴趣的:(BZOJ除草记)