noip2014 提高组题解 equation

题目大意:

给定高次方程an*x^n+...+a1*x^1+a0=0 求[1,m]区间内有多少个整数根

ai<=10^10000,m<=1000000,n<=100


首先,在考试的时候由于我很弱,一看到ai的范围我就傻了,果断30分骗起


解题思路:

1.30分

    30分很好骗,直接暴力就行了

2.60分

    因为我考试的时候写的是30分,我也没去写60分,听说好像高精度压位然后暴力可以,我也不知道是不是真的,但是估计差不多,可能要用到秦九韶/霍纳法则优化(就是用线性的时间求出对于一个x方程的值,不会的请自己查阅)我觉得没必要研究这个60分写法


3.100分

    (反正我考试的时候想不出来)但是我们学校有一个人想出来了Orz首先我们要知道一个性质,对于一个高次方程的系数取模,设取模的数为k,假设我们令之后的式子左边为f(x),那么原方程的解x0一定满足f(x0%k)%k=0。证明的话就不提供了,请读者自己查阅(其实是我不会)。

    所以我们只需要随机一个质数k,然后对原方程的系数取模(一位一位的读,操作第i位时需要把前面的操作得到的数乘以10然后加上第i位与1或-1乘积的后再取模k),取模之后的方程必须满足存在取模后的系数不为0,如果取模后系数都为0,就无意义了,如果随机到的质数使所有的系数都为0,那么请重新随机k

    之后我们从1~MIN(k,m)中枚举对于取模后的方程f'满足f'(x)%k=0的x,之后将每一个得到的数加上k的t倍(k*t要小于m),显然这些也满足f'(x)%k=0,这样我们就可以求出包含所有原方程解的一些数(显然这些数有些不是原方程的解,只是取模后。。。。。),我们称之为当前的解

    然后在不断的随机质数w对方程的系数取模,然后将当前的解再带入新的取模方程中判断f''(x)%w=0,如果不满足,就将其踢出当前的解,我们发现,如果是原方程的解,无论怎样,都不会被这样筛选掉

    当然,不断的筛选需要卡时,当你筛选的时间达到900ms是就直接break,这样基本(其实就是几乎100%)上就是答案了。

    一个比较重要的问题就是质数的选择,如果选择的质数k太大,那么在第一次寻找当前的解时,将会出现k过大使得时间复杂度达到O(kn),当k过大时就会超时,如果k太小那么就会出现数据将范围内的所有质数乘起来使得你不管怎么找都找不到满足要求的k,这样你就滚大粗了。所以找到一个合适的质数是必要的。我觉得可以选择在10000~100000之间的质数,因为把所有的乘起来比10^10000大,没法使得无论你怎么找都方程系数全部为0

    解决了前面一个问题之后,还要选择质数w,如果质数w过小,那么将无法筛去错解,如果w过大。。。。。我也不知道会怎样。

    反正我取的还是10000~100000间的(为了偷懒不想再筛一遍质数),反正基本上不会出错,但是我建议大家不要学我,尽量选择10^6~10^7之间的数

    经过测试,极限数据大概可以随机w筛选200余次,基本上不会错(如果还错的话。。。。。。请去买彩票。。。。。。。。。)

    对于筛素数,我推荐大家去用线性的欧拉筛,因为毕竟10^7比较大,如果准备像我一样的请忽视。。。。。。。。。。。。



下面附上我修改后水过民间数据的程序(写得丑不要吐槽...........变量名请不要吐槽。。。。)


</pre><pre name="code" class="cpp">//本人是淳朴的C党
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#define MAX(a,b) (a>b?a:b)
#define MIN(a,b) (a>b?b:a)

int n,m;
int moda[11000]={0};
int ans[10100]={0};
int ansp=0;
char a[110][10010]={"\0"};
int len[110]={0};
int hash[100010]={0};
int f[1000010]={0};
int zs[10000]={0};
int zss=0;
int mod;

int main()
{
    int i,j,p,q,r;
    int k;
    int t=0;
    freopen("equation.in","r",stdin);
    freopen("equation.out","w",stdout);
    srand((unsigned)time(NULL));
    scanf("%d%d\n",&n,&m);
    for(i=0;i<=n;i++)
    {
    	fgets(a[i],sizeof(a[i]),stdin);
    	len[i]=strlen(a[i]);
    	for(;a[i][len[i]-1]=='\n' || a[i][len[i]-1]=='\r';a[i][len[i]-1]='\0',len[i]--);
	}
	for(i=2;i<=100000;i++)
	{
		if(hash[i]==0)
		{
			for(j=2;i*j<=100000;j++)
				hash[i*j]=1;
			if(i>=10000)
				zs[++zss]=i;
		}
	}
	
	for(;;)
	{
		mod=zs[rand()%zss+1];
		p=0;
		for(i=0;i<=n;i++)
		{
			k=1;
			moda[i]=0;
			for(j=1;j<=len[i];j++)
			{
				if(a[i][j-1]=='-')
				{
					k=-1;
					continue;
				}
				else moda[i]=(moda[i]*10+k*(a[i][j-1]-'0'))%mod;
			}
			if(moda[i]!=0)
				p++;
		}
		if(p>0)
			break;
	}
	
	for(i=1;i<=mod && i<=m;i++)
	{
		p=0;
		for(j=n;j>=0;j--)
			p=((long long)p*i+moda[j])%mod;
		if(p%mod==0)
			ans[++ansp]=i;
	}
	
	for(i=1;i<=ansp;i++)
	{
		if(ans[i]+mod>m)
			break;
		else ans[++ansp]=ans[i]+mod;
	}
	
	for(t=1;clock()<=900;t++)
	{
		for(;;)
		{
			mod=zs[rand()%zss+1];
			p=0;
			for(i=0;i<=n;i++)
			{
				k=1;
				moda[i]=0;
				for(j=1;j<=len[i];j++)
				{
					if(a[i][j-1]=='-')
					{
						k=-1;
						continue;
					}
					else moda[i]=(moda[i]*10+k*(a[i][j-1]-'0'))%mod;
				}
				if(moda[i]!=0)
					p++;
			}
			if(p>0)
				break;
		}
		p=ansp;
		ansp=0;
		for(i=1;i<=p;i++)
		{
			r=0;
			for(j=n;j>=0;j--)
				r=((long long)r*ans[i]+moda[j])%mod;
			if(r%mod==0)
				ans[++ansp]=ans[i];
		}
	}
	
	printf("%d\n",ansp);
	for(i=1;i<=ansp;i++)
		printf("%d\n",ans[i]);
	
    fclose(stdin);
    fclose(stdout);
    return 0;
}


 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值