[NOI2001] 方程的解数
题目描述
已知一个
n
n
n 元高次方程:
∑
i
=
1
n
k
i
x
i
p
i
=
0
\sum\limits_{i=1}^n k_ix_i^{p_i} = 0
i=1∑nkixipi=0
其中:
x
1
,
x
2
,
…
,
x
n
x_1, x_2, \dots ,x_n
x1,x2,…,xn 是未知数,
k
1
,
k
2
,
…
,
k
n
k_1,k_2, \dots ,k_n
k1,k2,…,kn 是系数,
p
1
,
p
2
,
…
p
n
p_1,p_2,…p_n
p1,p2,…pn 是指数。且方程中的所有数均为整数。
假设未知数 x i ∈ [ 1 , m ] ( i ∈ [ 1 , n ] ) x_i \in [1,m] \space ( i \in [1,n]) xi∈[1,m] (i∈[1,n]),求这个方程的整数解的个数。
输入格式
第一行一个正整数
n
n
n,表示未知数个数。
第二行一个正整数
m
m
m。
接下来
n
n
n 行,每行两个整数
k
i
,
p
i
k_i,p_i
ki,pi。
输出格式
输出一行一个整数,表示方程解的个数。
样例 #1
样例输入 #1
3
150
1 2
-1 2
1 2
样例输出 #1
178
提示
【数据范围】
对于
100
%
100\%
100% 的数据,
1
≤
n
≤
6
1\le n \le 6
1≤n≤6,
1
≤
m
≤
150
1\le m \le 150
1≤m≤150,且
∑
i
=
1
n
∣
k
i
m
p
i
∣
<
2
31
\sum\limits_{i=1}^n |k_im^{p_i}| < 2^{31}
i=1∑n∣kimpi∣<231
答案不超过
2
31
−
1
2^{31}-1
231−1,
p
i
∈
N
∗
p_i \in \mathbb N^*
pi∈N∗。
思路
在这个题目中,暴力做法是枚举每一个xi,但是这种做法并不高效,会导致超时。
但是可以发现,在枚举每一个si的过程中,不同的si并不影响他后面的取值范围,所以可以从中间分开操作,先算前一半再算后一半,这样可以降低时间复杂度,只需要对比前一半和后一半有多少种可能互为相反数即可
完整代码
#include<bits/stdc++.h>
using namespace std;
int n,m,tot;
long long ans;
int k[3000],p[3000],b[10000000];
void dfsr(int i,int sum)//i表示当前为第几个数,sum表示当前各项的和
{
if(i==n)
{
b[++tot]=-sum;
return;
}//如果前一半已经抉择完了就把他的值的相反数储存进数组里
for(int x=1;x<=m;x++)//在题目允许范围内枚举x的值
{
int t=k[i];
for(int j=1;j<=p[i];j++)
{
t*=x;
}//计算
dfsr(i+1,sum+t);//递归处理
}
}
void dfsl(int i,int sum)
{
if(i==n/2)//当前一半已经搜索完了时
{
int x=sum;
ans+=upper_bound(b+1,b+tot+1,x)-lower_bound(b+1,b+tot+1,x);
//upper_bound表示的是大于等于x的第一个数的地址,lower_bound表示的是大于x的第一个数的地址
//两者相减就得到了等于x的数的个数,ans储存的是方案数,所以直接把相等的数加进去
return;
}
for(int x=1;x<=m;x++)
{
int t=k[i];
for(int j=1;j<=p[i];j++)
{
t*=x;
}
dfsl(i+1,sum+t);
}
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>k[i]>>p[i];//输入
dfsr(n/2,0);//先计算后一半
sort(b+1,b+tot+1);//对后一半的值进行排序,方便后面的操作
dfsl(0,0);//计算前一半并进行比较
cout<<ans;
return 0;
}