HNOI2002跳蚤--容斥原理

题目描述:读入n,m两个数,假设一个合法的数列是n+1位,且前n位不大于m,第n+1位为m。对于每一个数列,跳蚤可以选取任意一个数列中的数k,往左或右走k步(可以走多次),若使用这一个数列跳蚤可以到达左边一步的位置,那么这一个数列就是可以完成任务的数列。现在需要求出可以完成任务的数列的总数。

分析:题目需要找出所有能够到达左边一步位置的方案总数,能够到达左边一步,就相当于数列中所有数的最大公约数为1(可以通过扩欧推出),那么我们就转化成这样一个问题,取n个不大于m的数(位置不同也算不同方案),使得他们和m的最大公约数为1。

想到这一步,我们可以把问题转化为:所有合法数列的总数(m个数放到n位,共有m的n次方种)减去数列的最大公约数大于1的总数。

我们可以这么做:将m质因数分解,枚举m的质因数t,那么1到m之间有m/t个t的倍数,把数列中全都是这些m/t个数的方案数减去即可。

然而这种方法是存在问题的,有可能会重复减去同一种方案(6的倍数会同时在2的倍数和3的倍数时都减),所以需要枚举m的所有质因数的组合方式,记录每种组合的元素个数为tot,乘积为tmp,若tot是奇数,那么减去m/tmp个数放到n位里的方案个数,否则加上m/tmp个数放到n位里的方案个数(共有m/tmp的n次方种),这就是大名鼎鼎的容斥原理啊!

代码还是很短的。

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
int a[30],n,m;
LL ans;
LL pow(LL x,int y){
    LL ans=1;
    while(y){
        if(y&1)ans*=x;
        x*=x;
        y>>=1;
    }
    return ans;
}
void solve(){
    int k=m;
    for(int i=2;i*i<=k;i++)
        if(k%i==0){
            a[++a[0]]=i;
            while(k%i==0)k/=i;
        }
    if(k>1)a[++a[0]]=k;
    for(LL i=1;i<(1<<a[0]);i++){
        int tmp=1,tot=0;
        for(int j=1;j<=a[0];j++)
            if(i&(1LL<<(j-1)))
                tot++,tmp*=a[j];
        if(tot&1)ans-=pow(m/tmp,n);
        else ans+=pow(m/tmp,n);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    ans=pow(m,n);
    solve();
    printf("%LLd\n",ans);
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值