[HNOI2002] 跳蚤

题目描述

Z城市居住着很多只跳蚤。在Z城市周六生活频道有一个娱乐节目。一只跳蚤将被请上一个高空钢丝的正中央。钢丝很长,可以看作是无限长。节目主持人会给该跳蚤发一张卡片。卡片上写有N+1个自然数。其中最后一个是M,而前N个数都不超过M,卡片上允许有相同的数字。跳蚤每次可以从卡片上任意选择一个自然数S,然后向左,或向右跳S个单位长度。而他最终的任务是跳到距离他左边一个单位长度的地方,并捡起位于那里的礼物。

比如当N=2,M=18时,持有卡片(10, 15, 18)的跳蚤,就可以完成任务:他可以先向左跳10个单位长度,然后再连向左跳3次,每次15个单位长度,最后再向右连跳3次,每次18个单位长度。而持有卡片(12, 15, 18)的跳蚤,则怎么也不可能跳到距他左边一个单位长度的地方。

当确定N和M后,显然一共有\(M^N\)张不同的卡片。现在的问题是,在这所有的卡片中,有多少张可以完成任务。

输入输出格式

输入格式:

输入文件有且仅有一行,包括用空格分开的两个整数N和M。

输出格式:

输出文件有且仅有一行,即可以完成任务的卡片数。

\(1≤N≤M≤10^{8}\) ,且 \(MN≤10^{16}\)

输入输出样例

输入样例#1:

2 4

输出样例#1:

12

说明

这12张卡片分别是:

(1, 1, 4), (1, 2, 4), (1, 3, 4), (1, 4, 4), (2, 1, 4), (2, 3, 4),

(3, 1, 4), (3, 2, 4), (3, 3, 4), (3, 4, 4), (4, 1, 4), (4, 3, 4)

题解

有m种卡片,卡片上的数字可以使用无数次,使得最后的坐标在原点-1那么就可以看成
\(a*x_1+b*x_2+c*x_3+...+m*x_{n+1}=1\),其中\(x_{n+1}\)=M 那么也就可以推出
\(GCD(x_1,x_2,x_3...x_{n+1})=1\) ,也就是这n+1个元素都互质
那么我们的初始答案就是ans=\(M^n\),然后减去这些数中公约数的方案

那么我们可以枚举1~m中m的因子,那么就有m/t个t的倍数,把数列中全都是这些m/t个数的方案数减去即可
但是这种方法时候有问题的,我们可能会减去多次同种方案

那么我们就可以用容斥原理求出答案
ans=\(m^n\)-(有公因数2的n元组)-(有公因数3的n元组)-(有公因数5的n元组)+(有公因数2,3的n元组)+(有公因数2,5的n元组)+(有公因数3,5的n元组)-(有公因数2,3,5的n元组)+...

code

#include<bits/stdc++.h>
#define in(i) (i=read())
using namespace std;
typedef long long lol;
lol read() {
    lol ans=0,f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') {ans=(ans<<1)+(ans<<3)+i-'0'; i=getchar();}
    return ans*f;
}
lol n,m,ans,cnt;
lol p[1000010];
lol qpow(lol a,lol x) {
    lol ans=1;
    while(x) {
        if(x&1) ans*=a;
        x>>=1;
        a*=a;
    }
    return ans;
}
void divide(lol m) {
    for(lol i=2;i*i<=m;i++) {
        if(m%i==0) {
            p[++cnt]=i;
            while(m%i==0) m/=i;
        }
    }
    if(m>1) p[++cnt]=m;
}
void dfs(lol x,lol sum,lol step) {
    if(x>cnt) {
        if(!step) return;
        if(step&1) ans-=qpow(m/sum,n);
        else ans+=qpow(m/sum,n);
        return;
    }
    dfs(x+1,sum,step);
    dfs(x+1,sum*p[x],step+1);
}
int main()
{
    in(n); in(m);
    ans=qpow(m,n); divide(m);
    dfs(1,1,0);
    cout<<ans<<endl;
}
博主蒟蒻,随意转载.但必须附上原文链接
http://www.cnblogs.com/real-l/

转载于:https://www.cnblogs.com/real-l/p/9353367.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值