[NOIP2014] 解方程

题目描述

已知多项式方程:

\[a_0+a_1x+a_2x^2+⋯+a_nx^n=0 \]

求这个方程在 [1,m] 内的整数解(n 和 m均为正整数)。

输入输出格式

输入格式:

共 n+2行。

第一行包含 2 个整数 n,m,每两个整数之间用一个空格隔开。

接下来的 n+1行每行包含一个整数,依次为 a0,a1,a2…an。

输出格式:

第一行输出方程在 [1,m] 内的整数解的个数。

接下来每行一个整数,按照从小到大的顺序依次输出方程在 [1,m] 内的一个整数解。

输入输出样例

输入样例#1:

2 10
1
-2
1

输出样例#1:

1
1

输入样例#2:

2 10
2
-3
1

输出样例#2:

2
1
2

输入样例#3:

2 10
1
3
2

输出样例#3:

0

说明

对于 30% 的数据:\(0<n≤2,∣a_i∣≤100,a_n≠0,m<100\).
对于 50% 的数据:\(0<n≤100,∣a_i∣≤10^{100},a_n≠0,m<100\)
对于 70% 的数据:\(0<n≤100,∣a_i∣≤10^{10000},a_n≠0,m<10^4\)
对于 100% 的数据:\(0<n≤100,∣a_i∣≤10^{10000},a_n≠0,m<10^6\)

Solution

首先我们得明白一条性质,如果我们选一个质数p,如果枚举x,发现多项式结果ans%p≠0,那么我们就能肯定x不是解,因为如果是解,那么多项式答案应该为0,但是0模任何数(除0之外)都为0,而此时发现模p不等于0,那么显然x不是解,看一波复杂度,O(NM)貌似可以过70分。继续想,
既然x不是解那么显然x+p也不是解,这样问题就解决了。
prime[i],代表第i个质数是什么。
但是又不能枚举去筛,那复杂度又上去了,所以可以用数组存下来,\(vis[i][j]\)代表i这个数取模第j个质数不等于0,那么到时枚举解x时,只需判断\(vis[x~~mod ~~prime[j]][j]\)是否等于0就行了,为了怕出错,多选几个质数就行了。
本题数据范围这么大,有点操蛋,这是要写高精度的节奏,死活不打。
发现可以一边读入一边取模。
这题数据有点强,卡1~200所有质数,所以我就选了一千多的质数。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ll;
char s[101][10010];
ll prime[]={2,7,9,11,103,1597,1601,1607,1609,1613,1619,1621,1627};
ll real[1010000],kzj=0;
ll p[110][20],vis[120][20];
ll ans[1001010];
ll check(char *ss,ll mod)
{
	ll flag=1,tot=0,x=0;char ch=ss[0];
	ll len=strlen(ss);
	if(ch<'0'||ch>'9') if(ch=='-') flag=-1,tot++,ch=ss[1];
	while(ch<='9'&&ch>='0') 
	{
		x=((x*10)%mod+(ch-48))%mod;
		ch=ss[++tot];
		if(tot==len)
		break;
	}
	return (x*flag+mod)%mod;
}
int main()
{	
//	freopen("xuanxue.in","r",stdin);
//	freopen("xuanxue.out","w",stdout);
	ll n,m;
	cin>>n>>m;
	n++;
	for(ll i=1;i<=n;i++)
	scanf("%s",s[i]);
	for(ll i=1;i<=n;i++)
	{
		for(ll j=0;j<=12;j++)
		p[i][j]=check(s[i],prime[j]);
	}
	for(ll i=1;i<=1700;i++)
	{
		ll flag=0;
		for(ll j=0;j<=12;j++)
		{
			ll k=1,sum=0;
			ll mod=prime[j];
			for(ll t=1;t<=n;t++)
			{
				sum=(sum%mod+p[t][j]%mod*k%mod)%mod;	
				k=k*i%mod;
			}
			if(sum!=0) 
			{
				vis[i][j]=1;
			}
		}
	}
	for(ll i=1;i<=m;i++)
	{
		ll flag=0;
		for(ll j=0;j<=12;j++)
		if(vis[i%prime[j]][j]==1)
		flag=1;
		if(!flag) real[++kzj]=i;
	}
	cout<<kzj<<endl;
	for(ll i=1;i<=kzj;i++)
	printf("%lld\n",real[i]);
}
posted @ 2018-08-31 18:16  k-z-j  阅读(270)  评论(0编辑  收藏  举报