这道题意思不难理解,可以认为是求满足方程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
比如当N=2,M=18时,持有卡片(10, 15, 18)的跳蚤,就可以完成任务:他可以先向左跳10个单位长度,然后再连向左跳3次,每次15个单位长度,最后再向右连跳3次,每次18个单位长度。而持有卡片(12, 15, 18)的跳蚤,则怎么也不可能跳到距他左边一个单位长度的地方。
当确定N和M后,显然一共有M^N张不同的卡片。现在的问题是,在这所有的卡片中,有多少张可以完成任务。
Input
Output
Sample Input
2 4
Sample Output
12
Hint
(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)