2023集 折半搜索 P5691 [NOI2001] 方程的解数

[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=1nkixipi=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 1n6 1 ≤ m ≤ 150 1\le m \le 150 1m150,且
∑ i = 1 n ∣ k i m p i ∣ < 2 31 \sum\limits_{i=1}^n |k_im^{p_i}| < 2^{31} i=1nkimpi<231
答案不超过 2 31 − 1 2^{31}-1 2311 p i ∈ N ∗ p_i \in \mathbb N^* piN

思路

在这个题目中,暴力做法是枚举每一个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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值