POJ 1091 跳蚤 解题报告(2014携程第一场A题,聪明的猴子)(容斥原理)

96 篇文章 0 订阅

跳蚤
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 7929 Accepted: 2309

Description

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张不同的卡片。现在的问题是,在这所有的卡片中,有多少张可以完成任务。 

Input

两个整数N和M(N <= 15 , M <= 100000000)。

Output

可以完成任务的卡片数。

Sample Input

2 4

Sample Output

12

Hint

这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) 

    好吧,今天携程的A题,没有弄出来。看到很多人做出来,感觉不是不难的。事实上的确不难,我理解的题意是每次按牌上数字往左或者往右跳,跳完后看下个牌。但真实的题意是上面这样,每张牌可以重复使用。重复使用时只要n+1个数互质即可,也就是gcd=1。也就是总数减去存在共因子的。最后一个数是确定的,可以对其进行因式分解,用容斥原理枚举公因子,奇加偶减。POJ上的题M比较大,所以用到了大数模版,事实上不用也可以A。

    好吧,给我的教训就是以后题目一定一定要多看几遍,不清楚要和队友讨论下。代码如下,聪明的猴子的话要改下输入那里。

/*
 * 大数模版
 * 完美支持负数及加减法操作
 * 支持乘法,大小比较,赋值
 * 支持与long long类型的赋值比较
 * SF-_- 14.04.01
 */
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int K = 10000;    // 数组里每位代表1W
const int M = 300;       // 一共10位
const char show[] = "%04lld";

struct Bignum
{
    LL a[M*2];          // 大数数组
    int len;            // 长度
    bool negative;      // 正负

    Bignum()
    {
        clear();
    }

    void clear()
    {
        len=0;
        negative=false;
        memset(a, 0, sizeof(a));
    }

    Bignum(LL num)
    {
        *this=num;
    }

    Bignum operator=(LL num)
    {
        clear();
        if(num<0) negative=true, num=-num;
        while(num)
            a[len++]=num%K,num/=K;
        return *this;
    }

    Bignum(const Bignum& cmp)
    {
        memcpy(this, &cmp, sizeof(Bignum));
    }

    Bignum operator=(const Bignum& cmp)
    {
        memcpy(this, &cmp, sizeof(Bignum));
        return *this;
    }

    int absCmp(const Bignum& cmp)
    {
        if(len!=cmp.len)
            return len>cmp.len?1:-1;

        for(int i=len-1;i>=0;i--)
            if(a[i]!=cmp.a[i])
                return a[i]>cmp.a[i]?1:-1;

        return 0;
    }

    int absCmp(LL num)
    {
        Bignum cmp(num);
        return absCmp(cmp);
    }

    bool operator<(const Bignum& cmp)
    {
        if(negative^cmp.negative)
            return negative?true:false;

        if(negative)
            return absCmp(cmp)>0;
        else
            return absCmp(cmp)<0;
    }

    bool operator<(LL num)
    {
        Bignum cmp(num);
        return *this<cmp;
    }

    bool operator==(const Bignum& cmp)
    {
        if(negative^cmp.negative)
            return false;
        return absCmp(cmp)==0;
    }

    bool operator==(LL num)
    {
        Bignum cmp(num);
        return *this==cmp;
    }

    void absAdd(const Bignum& one, const Bignum& two)
    {
        len=max(one.len, two.len);
        for(int i=0;i<len;i++)
        {
            a[i]+=one.a[i]+two.a[i];
            if(a[i]>=K) a[i]-=K, a[i+1]++;
        }
        if(a[len]) len++;
    }

    void absSub(const Bignum& one, const Bignum& two)
    {
        len=one.len;
        for(int i=0;i<len;i++)
        {
            a[i]+=one.a[i]-two.a[i];
            if(a[i]<0) a[i+1]--,a[i]+=K;
        }
        while(len>0 && a[len-1]==0) len--;
    }

    void absMul(const Bignum& one, const Bignum& two)
    {
        len=one.len+two.len;
        for(int i=0;i<one.len;i++) for(int j=0;j<two.len;j++)
            a[i+j]+=one.a[i]*two.a[j];
        for(int i=0;i<len;i++) if(a[i]>=K)
            a[i+1]+=a[i]/K,a[i]%=K;
        while(len>0 && a[len-1]==0) len--;
    }

    Bignum operator+(const Bignum& cmp)
    {
        Bignum c;
        if(negative^cmp.negative)
        {
            bool res = absCmp(cmp)>0;
            c.negative = !(negative^res);
            if(res)
                c.absSub(*this, cmp);
            else
                c.absSub(cmp, *this);
        }
        else if(negative)
        {
            c.negative=true;
            c.absAdd(*this, cmp);
        }
        else
        {
            c.absAdd(*this, cmp);
        }
        return c;
    }

    Bignum operator-(const Bignum& cmp)
    {
        Bignum cpy;
        if(cpy==cmp)
            return *this;
        else
            cpy=cmp, cpy.negative^=true;

        return *this+cpy;
    }

    Bignum operator*(const Bignum& cmp)
    {
        Bignum c;
        if(c==cmp || c==*this)
            return c;

        c.negative = negative^cmp.negative;
        c.absMul(*this, cmp);
        return c;
    }

    void output()
    {
        if(len==0)
        {
            puts("0");
            return;
        }

        if(negative)
            printf("-");

        printf("%lld", a[len-1]);
        for(int i=len-2;i>=0;i--)
            printf(show, a[i]);
        puts("");
    }
};

Bignum quickPow(Bignum a, LL b)
{
    Bignum res = 1;
    while(b)
    {
        if(b&1)
            res = res*a;
        a=a*a;
        b>>=1;
    }

    return res;
}

int factor[33];
int factorNum;

void decompostion(int n)
{
    factorNum = 0;
    int m=(int)(sqrt(n+0.0)+0.5);
    for(int i=2;i<=m;i++) if(n%i==0)
    {
        factor[factorNum++]=i;
        while(n%i==0) n/=i;
    }
    if(n>1) factor[factorNum++]=n;
}

void work(int n, int m)
{
    decompostion(m);

    Bignum ans = quickPow(m, n);
    for(int i=1;i<(1<<factorNum);i++)
    {
        int tmp=1;
        bool flag=false;
        for(int j=0;j<factorNum;j++) if(i&(1<<j))
            tmp = tmp*factor[j], flag=!flag;
        if(flag)
            ans = ans - quickPow(m/tmp, n);
        else
            ans = ans + quickPow(m/tmp, n);
    }

    ans.output();
}

int main()
{
    int n, m;
    while(~scanf("%d%d", &n, &m))
        work(n, m);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值