1. 程式人生 > >【bzoj3751】[NOIP2014]解方程 數論

【bzoj3751】[NOIP2014]解方程 數論

解方程 light 高精度 發現 lag ont bzoj3 一行 一個空格

題目描述

已知多項式方程:

a0+a1*x+a2*x^2+...+an*x^n=0 求這個方程在[1,m]內的整數解(n和m均為正整數)。

輸入

第一行包含2個整數n、m,每兩個整數之間用一個空格隔開。 接下來的n+1行每行包含一個整數,依次為a0,a1,a2,...,an。

輸出

第一行輸出方程在[1,m]內的整數解的個數。

接下來每行一個整數,按照從小到大的順序依次輸出方程在[1,m]內的一個整數解。

樣例輸入

2 10
2
-3
1

樣例輸出

2
1
2


題解

真心不難的數論題

首先高精度FFT肯定是不可取的,那麽就必須取模。但是只有1個模數極有可能多解,所以多選幾個大質數模數,如果左邊的式子對所有模數取模都為0,則幾乎可以判定為原方程的解。

但是這樣時間復雜度為$O(nmt)$,其中t是模數個數,會TLE。

我們設$f(i,j)$表示當左面的一坨的x=i時對j取模得到的數,那麽顯然$f(i,j)=f(i+j,j)=f(i+2j,j)=...$。

所以我們只需要處理0~j-1的數即可,剩下的直接根據前面的推出來。

這樣的時間復雜度為$O(t(np+m))$,其中p為模數大小。

所以p不能太大,但是太小也會影響答案正確性,所以取20000左右的質數最合適。

Tip1:bzoj裏的1010000指的是1010000,所以a是高精度數(卡在這裏無數次qaq)

Tip2:bzoj這道題加強了(加多了)數據,要數據後發現有40個點,但是時間依然是10s,所以常數卡得很死,不能使用long long,模數最好只有3個等等。

#include <cstdio>
#include <cstring>
const int tot = 3;
int prime[3] = {20029 , 22277 , 23333}; 
int n , m , a[1000010][3] , ok[100010] , cnt[1000010];
char str[1000010];
bool judge(int x , int p)
{
	int i , sum = 0;
	for(i = n ; ~i ; i -- )
		sum = ((sum * x % prime[p] + a[i][p]) % prime[p] + prime[p]) % prime[p];
	return !sum;
}
void read(int c)
{
	scanf("%s" , str);
	int i , j , flag = 1 , l = strlen(str);
	if(str[0] == ‘-‘)
	{
		flag = -1;
		for(i = 0 ; i < l ; i ++ ) str[i] = str[i + 1];
		l -- ;
	}
	for(i = 0 ; i < tot ; i ++ )
	{
		int sum = 0;
		for(j = 0 ; j < l ; j ++ ) sum = (sum * 10 + str[j] - ‘0‘) % prime[i];
		a[c][i] = sum * flag;
	}
}
int main()
{
	int i , j , num = 0;
	scanf("%d%d" , &n , &m);
	for(i = 0 ; i <= n ; i ++ ) read(i);
	for(i = 0 ; i < tot ; i ++ ) 
	{
		memset(ok , 0 , sizeof(ok));
		for(j = 0 ; j < prime[i] ; j ++ )
			if(judge(j , i))
				ok[j] = 1;
		for(j = 1 ; j <= m ; j ++ ) cnt[j] += ok[j % prime[i]];
	}
	for(i = 1 ; i <= m ; i ++ )
		if(cnt[i] == tot)
			num ++ ;
	printf("%d\n" , num);
	for(i = 1 ; i <= m ; i ++ )
		if(cnt[i] == tot)
			printf("%d\n" , i);
	return 0;
}

【bzoj3751】[NOIP2014]解方程 數論