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

这里写图片描述

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值