1. 程式人生 > >NOIP2014 lgP2312 解方程(秦九韶演算法+hash)

NOIP2014 lgP2312 解方程(秦九韶演算法+hash)

題面

點這裡

題解

這題十分玄學,貌似想破頭都只有50分的暴力。
什麼牛頓迭代法、FFT各種牛B演算法好像都不行,高精度只有暴力分。

正解基於以下
n=0=>n%p=0
逆命題明顯不對,但是多搞幾個質數做p,都有n%p=0,那麼n就很大可能等於0,對吧?這不就類似於hash嘛。

然後多項式%p,可以把%扔進去,將係數取模,帶入時也取模。將0~p1帶進多項式用秦九韶演算法,然後每次就可以O(n)求出多項式的值。

於是我們就弄五個質數,每個質數要很適合AC,是什麼樣請參考程式碼。然後預處理出個表,f[i][j]代表第i個質數做pjx,所求出的多項式%p的值。求出表後,1~m

列舉x0,然後在p之內的可以查表得到,在此之外的x0就先%p,因為顯然f(x)%p=f(x+p)%p=f(x+kp)%p,判斷是否所有選的質數為p都滿足要求就知道x0是不是零點了。

時間就是O(ncntpi+mcnt)cnt是選的質數的數量,pi則是它們的大小。

神奇的程式碼

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

using
namespace std; int n, m, a[5][105], f[5][20050], ans[105]; const int prime[] = {20011, 20021, 20023, 20029, 20047}; const int cnt = 5; void Read(int x){ bool f = true; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = false; ch = getchar();} int ret[5]; for(int i = 0; i < cnt; i++) ret[i] = 0
; while(ch >= '0' && ch <= '9'){ for(int i = 0; i < cnt; i++) ret[i] = ((ret[i] << 3) + (ret[i] << 1) + ch - '0') % prime[i]; ch = getchar(); } for(int i = 0; i < cnt; i++) a[i][x] = f ? ret[i] : prime[i] - ret[i]; } int calc(int t, int x){ int ret = 0; for(int i = n; i >= 0; i--) ret = (ret * x + a[t][i]) % prime[t]; return ret; } int main(){ freopen("lgP2312.in", "r", stdin); freopen("lgP2312.out", "w", stdout); scanf("%d%d", &n, &m); for(int i = 0; i <= n; i++) Read(i); for(int i = 0; i < cnt; i++) for(int j = 0; j < prime[i]; j++) f[i][j] = calc(i, j); for(int i = 1; i <= m; i++){ bool ok = true; for(int j = 0; j < cnt; j++) if(f[j][i % prime[j]]) ok = false; if(ok) ans[++ans[0]] = i; } printf("%d\n", ans[0]); for(int i = 1; i <= ans[0]; i++) printf("%d\n", ans[i]); return 0; }

這裡寫圖片描述

答案是偶然?必然?
你曾經選擇的道路,才是真正的命運。
緊握在手心的希望也好、不安也罷,
必定會化作驅使我們前進的光。