您的位置:首页 > 其它

NOIP 2014 D2T3 解方程 Hash大法好

2017-07-25 16:29 267 查看
题目大意:给定高次方程an*x^n+...+a1*x^1+a0*x^0=0 求[1,m]区间内有多少个整数根

ai<=10^10000。m<=100W

懒得高精,考场上写的long double乱搞……30分打底50分顶天QAQ

当我最终搞定了各种非官方数据之后,我仅仅能长跪大地。手捧鲜花。仰望上苍高喊:哈希大法好!

首先阿贝尔在200年前告诉我们 五次以上方程没有求根公式 于是我们仅仅能枚举1~m 这个是100W

然后100W再加上1W位的精度 都不用运算直接就是跪…… 怎么办呢QAQ

哈希大法好!

令f(x)=an*x^n+...+a1*x^1+a0*x^0 易知若f(x)=0 则f(x) mod p=0

反之假设f(x) mod p=0 那么我们基本能够得出f(x)=0 p比較靠谱的时候碰撞率极低

所以我们把全部的ai都对p取模 然后对于每一个解O(n)验证就可以

这样是O(m*n)的 能够拿到70分 p比較靠谱的话不会挂

那么100分怎么办呢?

哈希大法好!

我们非常easy就会发现f(x+p) mod p=f(x) mod p

于是我们选择一个小一些的p。预处理出0~p-1全部的f(x),然后超过p的取模就可以

可是p不够大会挂啊!

于是我们多选择几个p 分别取一遍mod 仅仅有一个值对全部的p取模之后全都0才算作解

哈希大法好!

Hash Killer III至今无人AC就是在证明这个算法的正确性!

哈希万岁!哈希赛高!

哈希万年推!

时间复杂度O(nΣp+m) 当中Σp是选择的全部质数之和 一般选择1W左右的质数即可了

不知道为什么无论考场上拿了多少分仅仅要回来把题切了就算做精神AC了0.0……

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 110
using namespace std;
typedef long long ll;
const int prime[]={10007,11261,14843,19997,21893};
int n,m,stack[1001001],top;
ll a[M][5],f[21893][5];
inline ll F(int x,int j)
{
int i;
ll re=0;
for(i=n;~i;i--)
re=(re*x+a[i][j])%prime[j];
return re;
}
inline void Input(int x)
{
static char s[10100];
int i,j;
bool flag=false;
scanf("%s",s+1);
for(i=1;s[i];i++)
{
if(s[i]=='-')
flag=true;
else
for(j=0;j<5;j++)
a[x][j]=( (a[x][j]<<1) + (a[x][j]<<3) + s[i]-'0' )%prime[j];
}
if(flag)
for(j=0;j<5;j++)
a[x][j]=prime[j]-a[x][j];
}
int main()
{
int i,j;
cin>>n>>m;
for(i=0;i<=n;i++)
Input(i);

for(j=0;j<5;j++)
for(i=0;i<prime[j];i++)
f[i][j]=F(i,j);

for(i=1;i<=m;i++)
{
for(j=0;j<5;j++)
if(f[i%prime[j]][j])
break;
if(j==5)
stack[++top]=i;
}

cout<<top<<endl;
for(i=1;i<=top;i++)
printf("%d\n",stack[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: