Lougu P5691 & SSL1127 方程的解数【hash】

9 篇文章 0 订阅

在这里插入图片描述
这道题很难。


思路:

初看此题,题目要求出给定的方程解的个数,这个方程在最坏的情况下可以有 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值