这道题很难。
思路:
初看此题,题目要求出给定的方程解的个数,这个方程在最坏的情况下可以有
6
6
6个未知数,
而且次数由输入决定。这样就不能利用数学方法直接求出解的个数,
解的范围最多 150 150 150个数,因此能使用枚举法。最简单的思路是穷举所有未知数的取值,这样时间复杂度是 O ( M 6 ) O(M^6) O(M6) ,无法承受。
自然想到能否缩小枚举的范围呢?我们再次注意到M 的范围,若想不超时,似乎算法的复杂度上限应该是 O ( M 3 ) O(M^3) O(M3) 左右, 15 0 3 < 10000000 150^3 < 10000000 1503<10000000 。这就启示我们能否通过枚举3个未知数的值来找到答案呢?
这样,前一半式子的值
S
S
S 可以确定,这时只要枚举后
3
3
3个数的值,检查他们的和是否等于
−
S
-S
−S即可。
这样只相当于在
O
(
M
3
)
O(M^3)
O(M3)前面加了一个系数,当然还需要预先算出
1
1
1到
150
150
150的各个幂次的值。
这样,问题就被转化成了:
如何迅速的找到某个 S S S是否曾经出现过,以及出现过了多少次,
于是又变成了某个元素是否在给定集合中这个问题。所以,我们还是使用哈希表解决这个问题。还是把 S S S的值作为关键字使用除余法即可。
然而有一点需要注意,这个例子我们不仅需要纪录某个 S S S是否出现,出现的次数也很重要,所以可以用一个二维数组记录。
具体:
看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#define pm 4000037
using namespace std;
long long n,m,k[pm],p[pm];
long long h[pm][2],mid;
long long ans;
long long pow1(long long x,long long y) //手打pow
{
long long z=1;
for(long long i=1; i<=y; i++)
z*=x;
return z;
}
long long hash(long long x)
{
return x%pm;
}
long long dw(long long x)
{
long long hs=hash(abs(x));
while(h[hs][0]&&h[hs][0]!=x)
hs=(hs+1)%pm;
return hs;
}
void inhash(long long x)
{
long long hs=dw(x);
h[hs][0]=x;
h[hs][1]++;
}
long long dfs(long long dep,long long sum)
{
if(dep==mid)
for(int i=1; i<=m; i++)
inhash(sum+k[dep]*(pow1(i,p[dep])));
else
for(int i=1; i<=m; i++)
dfs(dep+1,sum+k[dep]*pow1(i,p[dep]));
}
long long dfs2(long long dep,long long sum)
{
if(dep==n)
{
for(int i=1; i<=m; i++)
{
long long js=-1*(sum+k[dep]*pow1(i,p[dep]));
if(h[dw(js)][0]==js)
ans+=h[dw(js)][1];
}
}
else
for(int i=1; i<=m; i++)
dfs2(dep+1,sum+k[dep]*pow1(i,p[dep]));
}
long long tp() //特判
{
if(n==1)
{
if(k[1]==0)
cout<<m;
else
cout<<0;
}
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1; i<=n; i++)
cin>>k[i]>>p[i];
mid=n/2;
tp();
if(n!=1)
{
dfs(1,0);
dfs2(mid+1,0);
cout<<ans;
}
return 0;
}