[BZOJ3930][CQOI2015]选数(反演+杜教筛)

本文针对一个具体的数学算法竞赛题目进行了解析,通过定义F(i)和f(i)来表示特定条件下的组合计数,并利用莫比乌斯反演求解F(k)。详细推导了计算过程,并给出了实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

我是超链接

题解:

以前好像这样干过,设F(i)表示在范围内选n个gcd为i的方案数,f(i)表示在范围内选n个i|gcd的方案数
首先 f(d)=d|nF(n) f ( d ) = ∑ d | n F ( n ) ,我们要求的是F(k)
反演 F(k)=k|df(d)μ(dk) F ( k ) = ∑ k | d ′ f ( d ′ ) μ ( d ′ k )
显然根据性质 f(d)=(HdL1d)n f ( d ) = ( H d − L − 1 d ) n
反演的柿子d’的上界是H,而且必须是k的倍数
设d’=kd,换元

F(k)=d=1Hkf(kd)μ(d)=d=1Hkμ(d)(HkdL1kd)n F ( k ) = ∑ d = 1 H k f ( k d ) μ ( d ) = ∑ d = 1 H k μ ( d ) ( H k d − L − 1 k d ) n

我们可以处理一下这里的H,L了,H=H/k,L=(L-1)/k,所以柿子是 F(k)=Hd=1μ(d)(HdLd)n F ( k ) = ∑ d = 1 H μ ( d ) ( H d − L d ) n
就是很明显的杜教筛了,然后用分块优化除法就好了

代码:

#include <map>
#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;
const int maxn=1e6;
const int N=1000005;
const int mod=1e9+7;
map<LL,LL>mp;
int pri[N],tot,mu[N];bool ss[N];
LL ksm(LL a,LL k)
{
    LL ans=1;
    for (;k;k>>=1,a=a*a%mod)
      if (k&1) ans=ans*a%mod;
    return ans;
}
void init(int n)
{
    mu[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (!ss[i]) pri[++tot]=i,mu[i]=-1;
        for (int j=1;j<=tot && pri[j]*i<=n;j++)
        {
            ss[pri[j]*i]=1;
            if (i%pri[j]==0) break;
            mu[pri[j]*i]=-mu[i];
        }
    }
    for (int i=1;i<=n;i++) mu[i]+=mu[i-1];
}
LL getmu(LL n)
{
    if (n<=maxn) return mu[n];
    if (mp.count(n)) return mp[n];
    LL ans=1;
    for (int i=2,last;i<=n;i=last+1)
    {
        last=n/(n/i);
        ans=(ans-getmu(n/i)*(last-i+1)%mod)%mod;
    }
    return mp[n]=ans;
}
int main()
{
    int n,k,L,H;
    scanf("%d%d%d%d",&n,&k,&L,&H);
    L--; H/=k; L/=k;
    init(min(H,maxn));LL ans=0;
    for (int i=1,last;i<=H;i=last+1)
    {
        if (L/i) last=min(H,min(H/(H/i),L/(L/i)));
        else last=min(H,H/(H/i));
        ans=(ans+ksm(H/i-L/i,n)*(getmu(last)-getmu(i-1)+mod)%mod)%mod;
    }
    printf("%lld",(ans+mod)%mod);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值