zoukankan      html  css  js  c++  java
  • [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后,显然一共有MN张不同的卡片。现在的问题是,在这所有的卡片中,有多少张可以完成任务。

    输入输出格式

    输入格式:

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

    输出格式:

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

    1≤M≤108,1≤N≤M,且MN≤1016。

    输入输出样例

    输入样例#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)

    题解:

    一张可行的卡片,就是将上面的数字经过加减变换能够得到1,于是我们可以猜想一下卡片上的数字所需满足的要求。首先很容易猜到,这些数字必然有奇数有偶数,但是仅满足这个条件是显然不够的,

    如3,6就无法得出1来,于是可以继续猜想这些数字两两互质,即所有数字的公因数为1,用数论的方法可以证明这个猜想是正确的。然后可以用容斥原理来算出答案,这里举个例子来说明,假设m=30=2*3*5,

    答案=m^n-(有公因数2的n元组)-(有公因数3的n元组)-(有公因数5的n元组)+(有公因数2,3的n元组)+(有公因数2,5的n元组)+(有公因数3,5的n元组)-(有公因数2,3,5的n元组)

    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long lol;
    lol n,m,ans,cnt,p[100001];
    lol qpow(lol a,lol b)
    {
        lol ans=1;
        while(b)
        {
            if(b&1)ans=ans*a;
            a=a*a;
            b>>=1;
        }
        return ans;
    }
    void make(lol m)
    {
        for(int 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;
    }
    bool b[100001];
    void dfs(lol x,lol a,lol k)
    {
        lol tot=0;
        if(x)
        {
            tot=pow(m/a,n);
            if(x&1)ans-=tot;
            else ans+=tot;
        }
        for(int i=k+1;i<=cnt;i++)
        {
            if(!b[i])
            {
                b[i]=1;
                dfs(x+1,a*p[i],i);
                b[i]=0;
            }
        }
    }
    int main()
    {
        lol i,j;
        scanf("%lld%lld",&n,&m);
        ans=qpow(m,n);
        make(m);
        dfs(0,1,0);
        printf("%lld",ans);
        return 0;
    }
  • 相关阅读:
    CUBRID学习笔记 41 sql语法之select
    CUBRID学习笔记 40 使用net修改数据
    CUBRID学习笔记 39 net使用dataset 返回查询的数据
    CUBRID学习笔记 38 net调用java的函数过程
    CUBRID学习笔记 36 在net中添加多行记录
    CUBRID学习笔记 37 ADO.NET Schema Provider
    CUBRID学习笔记 35 net驱动错误码和信息 cubrid教程示例
    程序员应该关注的一些事儿
    如何区分一个程序员是“老手“还是“新手“?
    10个调试和排错的小建议
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/7235240.html
Copyright © 2011-2022 走看看