P2312 [NOIP2014 提高组] 解方程
摘要:本文介绍了
题目看起来无处下手。但注意到高次方程没有求根公式,且题目只求整数解。我们不妨考虑将
此时又有一个问题:
于是我们只需要用到
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e2 + 5,M = 1e6 + 5;
const ll P = 1e9 + 9; // 一个大质数(这里不能取 1e9 + 7 会被卡)
int n,m,tot;
ll a[N],b[N];
bool ans[M];
int main(){
scanf("%d%d",&n,&m);
for(int i = 0;i <= n;++i){
char c = getchar();
bool flg = 0;
for(;!isdigit(c);c = getchar()) flg |= c == '-';
for(;isdigit(c);c = getchar()) a[i] = (a[i] * 10 + (c - '0')) % P; // 读入时预处理取模后结果
if(flg) a[i] = P - a[i];
}
for(int i = 0;i <= m;++i){
b[0] = 1;
for(int j = 1;j <= n;++j) b[j] = b[j - 1] * i % P; // 计算 b[j] = i 的 j 次方结果
ll f = 0;
for(int j = 0;j <= n;++j) (f += a[j] * b[j] % P) %= P; // 计算原多项式取模结果
if(f == 0) ans[i] = 1,++tot; // 存入答案
}
printf("%d\n",tot);
for(int i = 1;i <= m;++i) if(ans[i]) printf("%d\n",i);
return 0;
}
Bonus:秦九韶公式
我们发现预处理
于是我们从括号内往外求和:倒序枚举
更改后的求和部分代码:
for(int i = 0;i <= m;++i){
ll f = 0;
for(int j = n;j >= 0;--j) f = (f * i + a[j]) % P;
if(f == 0) ans[i] = 1,++tot;
}
Bonus:更优复杂度做法
我们不再枚举
注意到一件事:代入
这时我们再枚举
这样做的复杂度是多少呢?
注意到方程根至多有
将
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e2 + 5,M = 1e6 + 5;
const ll m1 = 1e9 + 9,m2 = 99929; // m1 是刚刚的 P, m2 是 C
int n,m,tot;
ll a1[N],a2[N]; // a1: a 数组模 m1 意义下结果,a2:模 m2 意义下结果
bool vis[m2],ans[M];
int main(){
scanf("%d%d",&n,&m);
for(int i = 0;i <= n;++i){
char c = getchar(); bool f = 0;
for(;!isdigit(c);c = getchar()) f |= c == '-';
for(;isdigit(c);c = getchar())
a1[i] = (a1[i] * 10 + (c - '0')) % m1,
a2[i] = (a2[i] * 10 + (c - '0')) % m2;
if(f) a1[i] = -a1[i],a2[i] = -a2[i];
}
for(int i = 0;i < m2;++i){ // 枚举 0 ~ C-1
ll k = 0;
for(int j = n;j >= 0;--j) k = (k * i + a2[j]) % m2;
vis[i] = k; // vis[i]: 模 C 余 i 的数是否被排除
}
for(int i = 0;i <= m;++i) if(!vis[i % m2]){ // 没排除的数再暴力验证
ll f = 0;
for(int j = n;j >= 0;--j) f = (f * i + a1[j]) % m1;
if(f == 0) ans[i] = 1,++tot;
}
printf("%d\n",tot);
for(int i = 1;i <= m;++i) if(ans[i]) printf("%d\n",i);
return 0;
}