bzoj 3930: [CQOI2015]选数

Description

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

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

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

解题报告:老套路设\(f_i\)表示gcd为i的方案数,转化为求[L/K,R/K]中互质的数对个数,答案即为\(f_1\),考虑这一题数据范围较大,且\(H-L<=10^5\),那么就需要用到结论:假设N个数不全相同,那么他们的最大公约数小于最大和最小的两个数之差,正确性显然,所以我们把状态改为:\(f_i\)表示选n个互不相同的数,gcd为i的方案数
令L=L/K,R=R/K
那么[L,R]间含i这个因子的数有(R/i)-((L-1)/i)种,所以选n个的方案数一共有\((R/i-(L-1)/i)^n\),再减去所有数都重复的\((R/i-(L-1)/i)\)种,减完之后的值设为\(tot\),然后就是推\(fi\),因为现在的方案只是包含i这个因子,而不是gcd=i的方案,所以还要减去gcd不为i的方案
综上:\(f_i=tot-\sum_{j=i*2}^{j=R-L}f_j\)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=1e5+5,mod=1000000007;
ll qm(ll x,ll k){
    ll sum=1;
    while(k){
        if(k&1)sum*=x,sum%=mod;
        x*=x;x%=mod;k>>=1;
    }
    return sum;
}
ll f[N];
void work()
{
    int n,L,R,K;
    cin>>n>>K>>L>>R;
    bool flg=(L<=K && K<=R);
    L--;L/=K;R/=K;int c=R-L;
    ll l,r;
    for(int i=c;i>=1;i--){
        l=L/i;r=R/i;
        f[i]=((qm(r-l,n)-(r-l)%mod)+mod)%mod;
        for(int j=2;j*i<=c;j++)
            f[i]-=f[j*i],f[i]=(f[i]%mod+mod)%mod;
    }
    f[1]+=flg;f[1]%=mod;
    printf("%lld\n",f[1]);
}

int main()
{
    work();
    return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/7466706.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值