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 个质数做 p j x ,所求出的多项式 %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 
#include 
#include 
#include 
#include 
#include 

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;
} 

NOIP2014 lgP2312 解方程(秦九韶算法+hash)_第1张图片

答案是偶然?必然?
你曾经选择的道路,才是真正的命运。
紧握在手心的希望也好、不安也罢,
必定会化作驱使我们前进的光。

你可能感兴趣的:(杂题,哈希)