poj 1091

来源:互联网 发布:淘宝卖家上传图片失败 编辑:程序博客网 时间:2024/06/13 01:10

      这道题意思不难理解,可以认为是求满足方程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 1000using 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) 

0 0
原创粉丝点击