[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∗。
大致思路
首先,把
∑
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
拆开得
∑
i
=
1
n
/
2
k
i
x
i
p
i
+
∑
i
=
n
/
2
+
1
n
k
i
x
i
p
i
=
0
\sum\limits_{i=1}^{n/2} k_ix_i^{p_i} +\sum\limits_{i=n/2+1}^{n} k_ix_i^{p_i} = 0
i=1∑n/2kixipi+i=n/2+1∑nkixipi=0
移项得
∑
i
=
1
n
/
2
k
i
x
i
p
i
=
−
∑
i
=
n
/
2
+
1
n
k
i
x
i
p
i
=
\sum\limits_{i=1}^{n/2} k_ix_i^{p_i} =-\sum\limits_{i=n/2+1}^{n} k_ix_i^{p_i} =
i=1∑n/2kixipi=−i=n/2+1∑nkixipi=
因此可以考虑折半搜索, meet in the middle
其次,对于幂运算,我们可以使用快速幂求解,效率更令人满意,快速幂代码如下
int quick_poww(int a,int b){
if(b==1)return a;
if(b%2==0){
int tmp=quick_poww(a,b/2);
return tmp*tmp;
}
else {
int tmp=quick_poww(a,b/2);
return tmp*tmp*a;
}
}
若有取模需求,在每次乘法后取模即可。
双dfs部分
对于查找部分,我们可以用基于Hash实现的unordered_map来实现迅速查找的功能,当然也可以手打Hash,set,upper_bound加lower_bound等,个人感觉还是unordered_map好用
以下代码中第一个dfs搜索前半段,后一个dfs搜索后半段,在后一个dfs中进行匹配
#define int long long int
int n,m,p[10],k[10];
int ans1[300],ans2[300],ans=0;
unordered_map<int,int> hass;
void dfs1(int cnt,int sum){
if(cnt==n/2+1){
hass[sum]++;
return;
}
for(int i=1;i<=m;i++){
dfs1(cnt+1,sum+k[cnt]*quick_poww(i,p[cnt]));
}
return;
}
void dfs2(int cnt,int sum){
if(cnt>n){
ans+=hass[-sum];
return;
}
for(int i=1;i<=m;i++){
dfs2(cnt+1,sum+k[cnt]*quick_poww(i,p[cnt]));
}
return;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>k[i]>>p[i];
}
dfs1(1,0);
dfs2(n/2+1,0);
cout<<ans<<endl;
return 0;
}
}
完整AC CODE
#include<bits/stdc++.h>
using namespace std;
#define int long long int
int n,m,p[10],k[10];
int ans1[300],ans2[300],ans=0;
unordered_map<int,int> hass;
int quick_poww(int a,int b){
if(b==1)return a;
if(b%2==0){
int tmp=quick_poww(a,b/2);
return tmp*tmp;
}
else {
int tmp=quick_poww(a,b/2);
return tmp*tmp*a;
}
}
void dfs1(int cnt,int sum){
if(cnt==n/2+1){
hass[sum]++;
return;
}
for(int i=1;i<=m;i++){
dfs1(cnt+1,sum+k[cnt]*quick_poww(i,p[cnt]));
}
return;
}
void dfs2(int cnt,int sum){
if(cnt>n){
ans+=hass[-sum];
return;
}
for(int i=1;i<=m;i++){
dfs2(cnt+1,sum+k[cnt]*quick_poww(i,p[cnt]));
}
return;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>k[i]>>p[i];
}
dfs1(1,0);
dfs2(n/2+1,0);
cout<<ans<<endl;
return 0;
}