Description
Input
第1行包含一个整数n。第2行包含一个整数M。第3行到第n+2行,每行包含两个整数,分别表示ki和pi。两个整数之间用一个空格隔开。第3行的数据对应i=1,第n+2行的数据对应i=n。
Output
仅一行,包含一个整数,表示方程的整数解的个数。
Sample Input
3
150
1 2
-1 2
1 2
Sample Output
178
hint
1<=n<=6;1<=M<=150
方程的整数解的个数小于231。
★本题中,指数Pi(i=1,2,……,n)均为正整数。
分析
本段摘自AJ的PPT。
分析:初看此题,题目要求出给定的方程解的个数,这个方程在最坏的情况下可以有6个未知数,而且次数由输入决定。这样就不能利用数学方法直接求出解的个数,而且注意到解的范围最多150个数,因此恐怕只能使用枚举法了。最简单的思路是穷举所有未知数的取值,这样时间复杂度是 O(M6) ,无法承受。因此我们需要寻找更好的方法,自然想到能否缩小枚举的范围呢?但是发现这样也有很大的困难。我们再次注意到M 的范围,若想不超时,似乎算法的复杂度上限应该是 O(M3) 左右,这是因为 1503 < 10000000 。这就启示我们能否仅仅通过枚举3个未知数的值来找到答案呢?如果这样,前一半式子的值 S 可以确定,这时只要枚举后3 个数的值,检查他们的和是否等于 -S 即可。这样只相当于在 O(M3) 前面加了一个系数,当然还需要预先算出 1 到 150 的各个幂次的值。想到了这里,问题就是如何迅速的找到某个 S 是否曾经出现过,以及出现过了多少次,于是又变成了"某个元素是否在给定集合中"这个问题。所以,我们还是使用哈希表解决这个问题。至于哈希函数不是问题,还是把 S 的值作为关键字使用除余法即可。然而有一点需要注意,这个例子我们不仅需要纪录某个 S 是否出现,出现的次数也很重要,所以可以用一个2维数组,仅仅是加了一个存储出现次数的域而已。
上代码
PS:又臭又长的蒟蒻code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<cmath>
typedef long long ll;
using namespace std;
const int p=4000037;
int n,m,mid;
ll ans;
int x[10],y[10];//x是系数,y是指数
struct node
{
int s,sum;//数及其个数
}a[p];
int hash(int x)
{
return x%p;//哈希函数
}
int locate(int k)//定位函数
{
int f=hash(abs(k));
int i=0;
while(i<p&&a[(f+i)%p].s!=0&&a[(f+i)%p].s!=k)
{
i++;
}
return (i+f)%p;
}
int ksm(int j,int k)//快速幂
{
int count=1;
while(k)
{
if(k&1)
{
count*=j;
}
j*=j;
k>>=1;
}
return count;
}
int putin(int k)//把k加入哈希表
{
int f=locate(k);
a[f].s=k;
a[f].sum++;
}
bool pd(int k)//判断k是否在哈希表中
{
if(a[locate(k)].s==k)
{
return true;
}
else
{
return false;
}
}
void dfs1(int dep,int k)//搜前半段
{
if(dep==mid)//搜完
{
for(int i=1;i<=m;i++)
{
putin(k+x[dep]*ksm(i,y[dep]));//调用快速幂
}
}
else
{
for(int i=1;i<=m;i++)
{
dfs1(dep+1,k+x[dep]*ksm(i,y[dep]));
}
}
}
void dfs2(int dep,int k)//搜后半段
{
if(dep==n)//搜完
{
for(int i=1;i<=m;i++)
{
long long t=-1*(k+x[dep]*ksm(i,y[dep]));
if(pd(t))
{
ans+=a[locate(t)].sum;//如果正好抵消,则加上其对应的数量
}
}
}
else
{
for(int i=1;i<=m;i++)
{
dfs2(dep+1,k+x[dep]*ksm(i,y[dep]));
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
}
mid=n/2;
if(n==1)//特判:如果只有一个未知数,且系数为0,则方程正好有m组解(x=1..m),否则无解
{
if(x[1]==0) cout<<m;
else cout<<0;
}
if(n!=1)
{
dfs1(1,0);//搜前半段
dfs2(mid+1,0);//搜后半段
cout<<ans;
}
return 0;
}