NOIP提高组2014——解方程

【题面】:
解方程

【思路】:
首先你会发现数据非常毒瘤,(a[i]<=10^{10000}),最开始以为要写高精度,等了要完模板之后(没错我这么辣鸡怎么会高精),才发现,根本用不着23333
因为其(n)比较小,可以考虑(hash)的思想,把(a[i])(\%)一个大质数,就避免了高精度,具体实现就是在读入的时候:

inline ll read(){
	ll x=0;char c;int f=1;c=getchar();
	while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){x = (x*10+c-48)%mod;c = getchar();}
	return (f*x)%mod;
}

然后考虑题目,(m<=1e6)(n<=100),感觉好像可以直接枚举,对于([1,m])的整数都(check)一遍,找出符合的答案。
但这样肯定超时,应为对于一个次数为(n)的多项式,求解时需要(frac{n(n+1)}{2})次乘法和(n)次加法,复杂度就变为了(O(nm+frac{nm(n+1)}{2})),无法承受,这个时候考虑秦九韶算法。
秦九韶算法:对于一个多项式(a_nx^n+a_{n-1}x^{n-1}+...+a_0),可以进行如下化简:

(a_nx^n+a_{n-1}x^{n-1}+...+a_0)
(=(a_nx^{n-1}+a_{n-1}x^{n-2}+...+a_2x+a_1)x+a_0)
(=((a_nx^{n-2}+a_{n-1}x^{n-3}+...+a_3x+a_2)x+a_1)x+a_0)
(.)
(.)
(.)
(=(...((a_nx+a_{n-1}x+a_{n-2}x+...+a_1)x+a_0)
则求解一个多项式的值,首先计算最内层括号内一次多项式的值,即:
(v_0=a_n)
(v_1=a_nx+a_{n-1})
然后由内向外逐层计算一次多项式的值,即
(v_2=v_1x+a_{n-2})
(v_3=v_2x+a_{n-3})
(.)
(.)
(.)
(v_n=v_{n-1}x+a_0)
这样,求(n)次多项式(f(x))的值就转化为求(n)个一次多项式的值。
结论:对于一个(n)次多项式,至多做(n)次乘法和(n)次加法。
搬运from度娘
然后复杂度就被成功地优化到了(O(nm)),虽然理论上还是过不了,但这样做也是正确并可以(AC)

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1000000007;

inline ll read(){
	ll x=0;char c;int f=1;c=getchar();
	while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){x = (x*10+c-48)%mod;c = getchar();}
	return (f*x)%mod;
}

const int MAXM = 1e6;
const int MAXN = 105;
ll a[MAXN];ll ans[MAXM];int num = 0;int n,m;

inline bool check(ll x){//秦九韶算法 
	ll v = a[n];
	for(int i=n-1;i>=0;--i){
		v = (v*x + a[i])%mod;
	}
	return v == 0;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n;++i){
		a[i] = read();
	}
	for(ll i=1;i<=m;++i){
		if(check(i)){
			ans[++num] = i;
		}
	}
	printf("%d
",num);
	for(int i=1;i<=num;++i) printf("%d
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/lajioj/p/9475674.html