P2312 解方程(秦九韶算法,模运算性质)

P2312 解方程

题目描述

给出一个形如 a 0 ​ + a 1 ​ x + a 2 ​ x 2 + ⋯ + a n ​ x n = 0 a_0​+a_1​x+a_2​x^2+⋯+a_n​x^n=0 a0+a1x+a2x2++anxn=0的多项式方程,求这个方程在 [ 1 , m ] [1,m] [1,m]区间内的所有整数解.

题目分析

分析出题人的意思并观察数据范围. 可见 ∣ a i ∣ |a_i| ai的数据范围非常大,而 m m m的数据范围却比较小(100w). 另外多达100项的一元多次方程,是没有通用解法的,而且用求导等数学方法也不现实,显然的规律同样没有. (包括导数等在内因为精度问题不方便计算,而且系数太大难以求导)同时,题目也说了:我们要输出的是满足条件的整数解. 这提示我们:可以通过枚举每一个数 i ( i < = m ) i(i<=m) i(i<=m),用 O ( 1 ) O(1) O(1)(或者说,一个 O ( 小常数 ) O(\text{小常数}) O(小常数))的时间判断是否满足条件,然后输出答案即可.

那么我们现在就需要一种快速的算法,判断等式是否成立.


秦九韶算法

实际上这个东西就是快读的基本思想.

百度百科:秦九韶算法

主要的思想如下:

a 0 ​ + a 1 ​ x + a 2 ​ x 2 + ⋯ + a n ​ x n a_0​+a_1​x+a_2​x^2+⋯+a_n​x^n a0+a1x+a2x2++anxn

= a 0 ​ + x ( a 1 ​ + a 2 ​ x + ⋯ + a n ​ x n − 1 ) =a_0​+x(a_1​+a_2​x+⋯+a_n​x^{n-1}) =a0+x(a1+a2x++anxn1)

= a 0 ​ + x ( a 1 ​ + x ( a 2 ​ + ⋯ + a n ​ x n − 2 ) ) =a_0​+x(a_1​+x(a_2​+⋯+a_n​x^{n-2})) =a0+x(a1+x(a2++anxn2))

= ⋯ =⋯ =

= a 0 + x ( a 1 + x ( a 2 + ⋯ x ( a n − 1 + x a n ) ) ) =a_0+x(a_1+x(a_2+⋯x(a_{n-1}+xa_n))) =a0+x(a1+x(a2+x(an1+xan)))

这样,我们在求一元高次多项式时的计算,就由原来朴素算法需要 n ( n + 1 ) / 2 n(n+1)/2 n(n+1)/2次乘法和 n n n次加法,减小为秦九韶算法的 n n n次乘法和 n n n次加法. 由于多项式最多只有 100 100 100项,所以显然每次判断都是一个常数.


模运算性质

我们解决了怎么在比较短的时间内判断方程有解的问题,接下来就得考虑另外一个问题了: ∣ a i ∣ < 1 0 10000 |a_i|<10^{10000} ai<1010000怎么办?

当然我们可以很无脑的算一个高精度,但是在时间业已非常紧张的情况下,这显然不佳. 我们想到了常用的做法:取模.

为什么呢?因为这个东西在输入(采用快读)的时候是可以预处理的,而且只要模数够大,模之后和依然等于0时就极有可能是一组正确解(当然这可能被卡,所以实在不放心可以多次取模),由模数基本性质可知,对于整道题而言正确性就没有影响.

我们知道模运算性质:
( a + b ) % m o d = ( a % m o d + b % m o d ) % m o d (a+b)\%mod=(a\%mod+b\%mod)\%mod (a+b)%mod=(a%mod+b%mod)%mod
( a ∗ b ) % m o d = ( a % m o d ∗ b % m o d ) % m o d (a*b)\%mod=(a\%mod*b\%mod)\%mod (ab)%mod=(a%modb%mod)%mod
所以对于题目中的式子:
a 0 ​ + a 1 ​ x + a 2 ​ x 2 + ⋯ + a n ​ x n = a 0 % m o d + a 1 % m o d   x + a 2 % m o d   x 2 + ⋯ + a n % m o d   x n = 0 a_0​+a_1​x+a_2​x^2+⋯+a_n​x^n=a_0\%mod+a_1\%mod\:x+a_2\%mod\:x^2+⋯+a_n\%mod\:x^n=0 a0+a1x+a2x2++anxn=a0%mod+a1%modx+a2%modx2++an%modxn=0,由于等式右边为0,所以取模之后等式仍然成立.

所以我们可以在输入数据时对一个大质数取模 19 ∗ ∗ ∗ ∗ 17 , 1 e 9 + 7 19****17,1e9+7 1917,1e9+7都可以,如果担心被卡也可以自己写个程序跑一个,只要超过一百万就行),然后从1到m暴力枚举,计算最后的值是否为0.


t i p s : tips: tips:如果担心跑不过的话快速乘亦可以加上.

程序实现

#include<bits/stdc++.h>
#define ll long long
#define maxn 110
#define maxm 1000010
using namespace std;
const ll p1=1e9+7;
inline ll read(ll mod){
	ll t=0,st=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')st=-1;ch=getchar();}
	while(isdigit(ch)){t=t*10+ch-'0';t%=mod;ch=getchar();}
	return st*t;//输入顺带取模
}
ll n,m,a[maxn];
int ans,out[maxm];
inline bool check(ll sum){
	ll tot=a[n],N=n;
	while(N){
		tot*=sum;
		tot%=p1;
		tot+=a[--N];
		tot%=p1;//这里也可以取模
	}//秦九韶算法,判断一个数是否符合要求
	if(!tot)return true;
	else return false;
}
int main(){
	n=read(p1),m=read(p1);
	for(ll i=0;i<=n;i++){
		a[i]=read(p1);
	}
	for(ll i=1;i<=m;i++){
		if(check(i)){
			ans++;
			out[ans]=i;
		}//暴力枚举每一个数
	}
	printf("%d\n",ans);
	for(int i=1;i<=ans;i++)
		printf("%d\n",out[i]);
	return 0;
} 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值