【ssl1127】方程的解数【较难的哈希表】【DFS深搜】

48 篇文章 0 订阅
10 篇文章 0 订阅

Description

在这里插入图片描述

Input

第1行包含一个整数n。第2行包含一个整数M。第3行到第n+2行,每行包含两个整数,分别表示ki和pi。两个整数之间用一个空格隔开。第3行的数据对应i=1,第n+2行的数据对应i=n。

Output

仅一行,包含一个整数,表示方程的整数解的个数。

Sample Input

3
150
1  2
-1  2
1  2

Sample Output

178
hint

1<=n<=6;1<=M<=150
在这里插入图片描述
方程的整数解的个数小于231
★本题中,指数Pi(i=1,2,……,n)均为正整数。

分析

本段摘自AJ的PPT。
分析:初看此题,题目要求出给定的方程解的个数,这个方程在最坏的情况下可以有6个未知数,而且次数由输入决定。这样就不能利用数学方法直接求出解的个数,而且注意到解的范围最多150个数,因此恐怕只能使用枚举法了。最简单的思路是穷举所有未知数的取值,这样时间复杂度是 O(M6) ,无法承受。因此我们需要寻找更好的方法,自然想到能否缩小枚举的范围呢?但是发现这样也有很大的困难。我们再次注意到M 的范围,若想不超时,似乎算法的复杂度上限应该是 O(M3) 左右,这是因为 1503 < 10000000 。这就启示我们能否仅仅通过枚举3个未知数的值来找到答案呢?如果这样,前一半式子的值 S 可以确定,这时只要枚举后3 个数的值,检查他们的和是否等于 -S 即可。这样只相当于在 O(M3) 前面加了一个系数,当然还需要预先算出 1 到 150 的各个幂次的值。想到了这里,问题就是如何迅速的找到某个 S 是否曾经出现过,以及出现过了多少次,于是又变成了"某个元素是否在给定集合中"这个问题。所以,我们还是使用哈希表解决这个问题。至于哈希函数不是问题,还是把 S 的值作为关键字使用除余法即可。然而有一点需要注意,这个例子我们不仅需要纪录某个 S 是否出现,出现的次数也很重要,所以可以用一个2维数组,仅仅是加了一个存储出现次数的域而已。

上代码

PS:又臭又长的蒟蒻code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<cmath>

typedef long long ll; 
using namespace std;

const int p=4000037;
int n,m,mid;
ll ans;
int x[10],y[10];//x是系数,y是指数 

struct node
{
	int s,sum;//数及其个数
}a[p];

int hash(int x)
{
	return x%p;//哈希函数
} 

int locate(int k)//定位函数
{
	int f=hash(abs(k));
	int i=0;
	while(i<p&&a[(f+i)%p].s!=0&&a[(f+i)%p].s!=k)
	{
		i++;
	}
	return (i+f)%p;
}

int ksm(int j,int k)//快速幂
{
	int count=1;
	while(k)
	{
		if(k&1) 
		{
			count*=j;
			
		}
		j*=j;
		k>>=1;
	}
	
	return count; 
}

int putin(int k)//把k加入哈希表
{
	int f=locate(k);
	a[f].s=k;
	a[f].sum++;
}

bool pd(int k)//判断k是否在哈希表中
{
	if(a[locate(k)].s==k)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void dfs1(int dep,int k)//搜前半段
{
	if(dep==mid)//搜完
	{
		for(int i=1;i<=m;i++)
		{
			putin(k+x[dep]*ksm(i,y[dep]));//调用快速幂 
		}
	}
	else
	{
		for(int i=1;i<=m;i++)
		{
			dfs1(dep+1,k+x[dep]*ksm(i,y[dep]));
		}
	}
}

void dfs2(int dep,int k)//搜后半段
{
	if(dep==n)//搜完
	{
		for(int i=1;i<=m;i++)
		{
			long long t=-1*(k+x[dep]*ksm(i,y[dep]));
			if(pd(t))
			{
				ans+=a[locate(t)].sum;//如果正好抵消,则加上其对应的数量
			}
		}
	}
	else
	{
		for(int i=1;i<=m;i++)
		{
			dfs2(dep+1,k+x[dep]*ksm(i,y[dep]));
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
	}
	mid=n/2;
	if(n==1)//特判:如果只有一个未知数,且系数为0,则方程正好有m组解(x=1..m),否则无解

	{
		if(x[1]==0) cout<<m;
		else cout<<0;
	}
	if(n!=1)
	{
		dfs1(1,0);//搜前半段
		dfs2(mid+1,0);//搜后半段 
	    cout<<ans;
	}
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值