Luogu P3172 [CQOI2015]选数

[CQOI2015]选数

Description

我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可

Input

输入一行,包含4个空格分开的正整数,依次为N,K,L和H

Output

输出一个整数,为所求方案数。

Sample Input

2 2 2 4

Sample Output

3

Answer

我们把左右区间分别处理:    r = r / k;    l = (l - 1) / k + 1;

这样问题就转化为求[l,r]区间内,选n个数其最大公约数为1的方案数

因为r-l<=105 所以这个区间内任意两个不相等的数的最大公约数最大不超过(r - l) <= 105 证明很好证:因为两个数p > q的最大公约数如果为x,那么p / x - q / x >= 1,那么p - q >= x、、、、、、

所以可以暴力所有的最大公约数,设[l,r]中选n个不完全相同的数其最大公约数为i的方案数为dp[i]

那么[l,r]中有多少含i因子的数呢?

显然有t = r / i - (l - 1) / i个数

那么dp[i] = tn 种方案,但是这里面肯定有n个数都相同的方案一共有t个,所以dp[i] = (tn - t) % mod;种方案

但是这个时候是所有n个数有i因子的方案数,而不是gcd=i的方案数,所以dp[i] = dp[i] - dp[j](j <= r - l && j | i)

然后这样dp[1]就是正解了

但是我们注意到,我们的dp[1]是在[l,r]中选n个不完全相同的数其最大公约数为i的方案数,但是实际上我们可以让n个数完全相同(假设为x),但是这样的话n个数的gcd就等于x,所以x必须为k,因为求的是gcd=k的方案数

所以当k∈[l,r]时,dp[1] = dp[1] + 1;

代码如下:

#include 
#define maxn (100000 + 10)
typedef long long int LLI;
using namespace std;
int dp[maxn];
int p = 1000000007;

LLI Fast_power(LLI n,LLI k,LLI mod) {
    LLI re = 1;
    n = n % mod;
    while(k) {
        if(k & 1)   re = re * n % mod;
        k = k >> 1;
        n = n * n % mod;
    }
    return re;
}

int main() {
//    freopen("in.txt","r",stdin);
    int l,r,n,k;
    bool flag = false;
    scanf("%d%d%d%d",&n,&k,&l,&r);
    if(k >= l && k <= r)    flag = true;
    r = r / k;
    l = (l - 1) / k;
    for(int i = r - l; i >= 1; i --) {
        int t = r / i - l / i;
        dp[i] = (Fast_power(t,n,p) - t + p) % p;
        for(int j = 2; j * i <= r - l; j ++)
            dp[i] = (dp[i] - dp[i * j] + p) % p;
    }
    if(flag == true)    dp[1] ++;
    printf("%d\n",dp[1] % p);
    return 0;
}

你可能感兴趣的:(数论)