poj 1091

      这道题意思不难理解,可以认为是求满足方程a1*X1+a2*X2+…+an*Xn=1的系数n元组(a1,a2,…,an)的个数,其中Xi的值为正值代表往左跳,为负值代表往右跳。

      要解决这道题,首先要知道a1*X1+a2*X2+…+an*Xn=d有解的充分必要gcd(a1,a2,…,an)|d。因此,此题要计算出所有满足gcd(a1,a2,…,an)|1的n元组(a1,a2,…,an)的个数。

      接下来就是计数的问题,枚举显然不是好方法,所以我们利用容斥原理计数(百度百科:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理)。结果 = (m ^ n) - (有公因数2的n元组)- (有公因数3的n元组)- (有公因数5的n元组)+ (有公因数2,3的n元组) +(有公因数2,5的n元组) + (有公因数3,5的n元组)- (有公因数2,3,5的n元组)(摘自:小花熊 poj 1091),在程序中可体现为m ^ n减去公因子数为奇数的,加上公因子数为偶数的。

     最后,就是枚举公因子的问题,这里可以利用二进制法。大概的思想是,二进制的每一位对应待枚举集合中的一个元素,1代表有,0代表没有,假设待枚举集合有n个元素,那么就可以将[0,1<<n)中的每一个二进制的组合代表集合的组合,就可以很方便枚举出所有公因子的组合。

     补充一下,这道题数据不强,所以用long long也能过掉,但是从题目给出的数据范围推断,应该用高精度运算才对。


代码(C++)(高精度):

#include <cstdlib>
#include <iostream>
#include <cmath>
#include <cstring>

#define MAX 1000

using namespace std;

class bign{
public:
    bign()
    {
        memset(number,0,sizeof(number));  
        len=0;  
    }
    bign(const bign &x)                   //拷贝构造函数 
    {
        memcpy(number,x.number,sizeof(x.number));
        len=x.len;
    }   
    friend bign operator+ (bign a,bign b) 
    {
        int i,k=0;
        bign tmp;

        tmp.len=a.len>b.len?a.len:b.len;
        for(i=0;i<tmp.len;i++)
        {
            if(i<a.len) tmp.number[i]+=a.number[i];
            if(i<b.len) tmp.number[i]+=b.number[i];
            k+=tmp.number[i];
            tmp.number[i]=k%10;
            k/=10;                  
        }
        while(k>0)
        {
           tmp.number[tmp.len++]=k%10;
           k/=10;
        }
        return tmp;   
    } 
    friend bign operator- (bign a,bign b)  //此题做减法不会出现负数,所以这里处理时不考虑有负的情况 
    {
        bign tmp=a; 
        int i,t;
        for(i=0;i<b.len;i++)
        {            
            tmp.number[i]-=b.number[i]; 
            t=0;                            
            while(tmp.number[i+t]<0)
            {
               tmp.number[i+t]+=10;
               t++;
               tmp.number[i+t]--;               
            }
        } 
        while(tmp.number[tmp.len-1]==0) tmp.len--; 
        return tmp;
    } 
    friend bign operator* (bign a,bign b) 
    {
        bign tmp;
        int i,j,k;
        int array[MAX];
        memset(array,0,sizeof(array)); 
        tmp.len=a.len;
        for(i=0;i<b.len;i++)
        {
            k=0;                
            for(j=0;j<a.len;j++)
            {
                k=k+array[j+i]+a.number[j]*b.number[i];
                array[j+i]=k%10;
                if(j+i>=tmp.len) tmp.len=j+i+1;
                k/=10;                
            }
            while(k>0)
            {
                array[tmp.len++]=k%10;
                k/=10;
            }
        } 
        memcpy(tmp.number,array,sizeof(array));  
        return tmp; 
    }
    friend bign operator^ (bign a,int b)
    {
        bign tmp=a;        
         
        while(--b)
        {
           tmp=tmp*a;  
        }
        return tmp; 
    }
    bign operator= (int x)
    {
         len=0;
         while(x>0)
         {
             number[len++]=x%10;
             x/=10;      
         }
         return *this;
    }
    bign operator= (bign a)
    {
         memcpy(number,a.number,sizeof(a.number));
         len=a.len;
         return *this;
    }
    void print()
    {
         int i;
         for(i=len-1;i>=0;i--) cout<<number[i];
         cout<<endl;
    }
private:
    int number[MAX];  //number[0]代表个位元素,以此类推 
    int len;                
};

int main(int argc, char *argv[])
{
    int n,m,t,c,x,y,k,i,j,prime[15];   //m的素因子个数保守估计不会超过12,因为11!<m<12! 
    bign ans,tmp;
    while(cin>>n>>m)
    {
        tmp=m;           
        ans=tmp^n;
        
        t=m;
        c=0;
        for(i=2;i<=sqrt(m*1.0);i++)      //找出m的素因子 
        {
            if(t%i==0)
            {
               prime[c++]=i;
               while(t%i==0) t/=i;               
            }
        }
        if(t!=1) prime[c++]=t;
        
        x=1<<c;                
        for(i=1;i<x;i++)
        {
            k=1;
            y=0;
            for(j=0;j<c;j++)
            {
               if(i&1<<j)
               {
                  k*=prime[j];
                  y++; 
               }
            } 
            tmp=m/k;
            if(y&1) ans=ans-(tmp^n);
            else ans=ans+(tmp^n);           
        } 
        ans.print(); 
        
    } 
    system("PAUSE");
    return EXIT_SUCCESS;
}

        本人平时不怎么写C++的类,可能代码风格不是很好,大家多多包涵,如果有什么建议,也请大牛们告诉我一下。


题目:

跳蚤
Time Limit: 1000MS Memory Limit: 10000K
   

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) 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值