[HEOI2012]Akai的数学作业-题解

版权声明:转载注明出处,谢谢,有问题可以向博主联系 https://blog.csdn.net/VictoryCzt/article/details/83419276

题目地址【IN

  • 题意简述

给你一个多项式方程,形式如下,求其所有的有理数解。

a 0 + a 1 x + a 2 x 2 + + a n x n = 0 a_0+a_1x+a_2x^2+\cdots+a_nx^n=0


  • 暴力

我们发现输出的是一个分子和分母互质的分数,所以我们枚举一下分子和分母,为了不超时,枚举大概 300 300 不到,然后每次计算一下(用高精),大概能得到 30 30 分。

  • 优化

我们由于用高精,复杂度比较高,所以我们考虑取模意义下,当我们多取几个模数,在这几个模数的意义下,算出来都是 0 0 ,那么也可以看作是答案,但是有一定错的概率,当模数比较大且为质数时不容易错,大概能拿 30 50 30\sim 50 分。

  • 分析

我们可以将开始的式子转换为分数形式,我们令 x = p q x=\frac{p}{q}

那么原式就可以写成:

a 0 + a 1 ( p q ) + a 2 ( p q ) 2 + + a n ( p q ) n = 0 a_0+a_1\left(\frac{p}{q}\right)+a_2\left(\frac{p}{q}\right)^2+\cdots+a_n\left(\frac{p}{q}\right)^n = 0
a 0 + a 1 ( p q ) + a 2 ( p 2 q 2 ) + + a n ( p n q n ) = 0 a_0+a_1\left(\frac{p}{q}\right)+a_2\left(\frac{p^2}{q^2}\right)+\cdots+a_n\left(\frac{p^n}{q^n}\right) = 0

接下来我们等式两端同时乘以 q n q^n ,那么可以得到如下式子:

a 0 q n + a 1 p q n 1 + a 2 p 2 q n 2 + + a n p n = 0 a_0q^n+a_1pq^{n-1}+a_2p^2q^{n-2}+\cdots+a_np^n=0

我们将式子两端同时模 q q ,那么可以得到:

a n p n 0 ( m o d   q ) a_np^n\equiv0(\rm mod\ q)

我们可以得到 q q 肯定为 a n a_n 的因子,因为 p , q p,q 互质,即 q a n q|a_n

我们再将原式两端同时模 p p ,那么同样可以得到:

a 0 q n 0 ( m o d   p ) a_0q^n\equiv0(\rm mod\ p)

我们同样可以的到 p p 肯定为 a 0 a_0 的因子,同理,即 p a 0 p|a_0

所以我们可得对于方程的解 x = p q x=\frac{p}{q} ,我们就得知了 p a 0 p|a_0 q a n q|a_n 并且还要满足 ( p , q ) = 1 (p,q)=1 (也就是题面要求的 p , q p,q 互质)。


  • 正解

有了上面的分析,我们就可以先预处理对于 a 0 a_0 a n a_n 的因子,但是这里有个问题就是如果 a 0 = 0 a_0=0 或者 a n = 0 a_n=0 ,那么我们对于 a 0 a_0 就换成左边第一个不为 0 0 的系数, a 1 a_1 换成右边第一个不为 0 0 的因子(由于系数是 0 0 ,所以前面的都可以相当于没有)。

然后我们可以枚举 p , q p,q ,由于 2 × 1 0 7 2\times 10^7 的因子数最多为 512 512 ,所以我们可以直接 51 2 2 512^2 枚举。

对于判断一个解 p q \frac{p}{q} 是否合法,我们可以取几个模数,然后按照优化的暴力方式判断一个解是否合法(其实有一个非常好的模数,只需这一个 50033 50033 ,就可以判断了)。

然后对于每个枚举的 p q \frac{p}{q} ,由于有负数的解,所以我们还要对每个枚举的 p q \frac{p}{q} 去判断 p q -\frac{p}{q}

然后我们用一个结构体写一个分数类,将答案存入,并重载小于排序,输出答案即可。

下面上代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int Mod=50033;
const int N=1010,M=110;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int fpow(int a,int b){int ans=1;for(;b;b>>=1,a=(1ll*a*a)%Mod)if(b&1)ans=(1ll*ans*a)%Mod;return ans;}
int in1[N],in2[N],c1,c2;
int A[M],n;
void init(){
	int a=0,b=0;
	for(int i=0;i<=n;i++)if(A[i]){a=A[i];break;}
	for(int i=n;i>=0;i--)if(A[i]){b=A[i];break;}
	if(a<0)a=-a;if(b<0)b=-b;
	int t1=sqrt(a),t2=sqrt(b);
	for(int i=1;i<=t1;i++){
		if(!(a%i)){
			in1[++c1]=i;
			if(a/i!=i)in1[++c1]=a/i;
		}
	}
	sort(in1+1,in1+c1+1);
	for(int i=1;i<=t2;i++){
		if(!(b%i)){
			in2[++c2]=i;
			if(b/i!=i)in2[++c2]=b/i;
		}
	}
	sort(in2+1,in2+c2+1);
}
struct node{
	int fz,fm;
	node(){}
	node(int a,int b):fz(a),fm(b){}
	bool operator <(const node &a)const{
		if((!fz||!fm)||(!a.fz||!a.fm)){
			if((!fz||!fm)&&(!a.fz||!a.fm)) return 1;
			if((!fz||!fm))return 0<a.fz;
			if((!a.fz||!a.fm))return fz<0;
		}
		ll t1=1ll*fz*a.fm,t2=1ll*a.fz*fm;
		return t1<t2;
	}
	void out(){
		if(!fz||!fm) puts("0");
		else if(fm==1) printf("%d\n",fz);
		else{
			int now=gcd(fz,fm);
			fz/=now;fm/=now;
			if(fm<0)fz=-fz,fm=-fm;
			printf("%d/%d\n",fz,fm);
		}
	}
}anss[M];
int tot;
void calc(int type,int p,int q){
	int ans=0,t1=1,t2=1,inv_q=fpow(q,Mod-2)%Mod;
	if(type)p=-p;
	for(int i=1;i<=n;i++)t1=(t1*q)%Mod;
	for(int i=0;i<=n;i++){
		ans=(ans+1ll*A[i]*t1%Mod*t2%Mod)%Mod;
		t1=(1ll*t1*inv_q)%Mod;
		t2=(1ll*t2*p)%Mod;
	}
	if(!ans){
		anss[++tot]=node(p,q);
	}else return;
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<=n;i++)scanf("%d",&A[i]);
	init();
	for(int i=0;i<=n;i++)A[i]%=Mod;
	for(int i=1;i<=c1;i++){
		for(int j=1;j<=c2;j++){
			if(gcd(in1[i],in2[j])==1){
				calc(0,in1[i],in2[j]);
				calc(1,in1[i],in2[j]);
			}
		}
	}
	if(A[0]==0)anss[++tot]=node(0,0);
	sort(anss+1,anss+tot+1);
	printf("%d\n",tot);
	for(int i=1;i<=tot;i++)anss[i].out();
	return 0;
}

下面为 h d x r i e \rm hdxrie 的讲解 O r z Orz

复数域下的一个关于 x x n n 次多项式 f ( x ) = a 0 + a 1 x + a 2 x 2 + a 3 x 3 + . . . + a n x n f(x)=a_0+a_1x+a_2x^2+a_3x^3+...+a_nx^n 一定可以分解成 n n 个含 x x 的一次多项式相乘,即 f ( x ) f(x) 一定存在一种形如 f ( x ) = ( b i x + c i ) f(x)=\prod(b_ix+c_i) 的表示,其中每个式子都会产生一个复数域下的根(当然,这些根有可能重复)。

我们只用考虑有理数根,所以可以把方程改写为 f ( x ) = g ( x ) × ( b i x + c i ) f(x)=g(x)\times\prod(b_ix+c_i) 的样子,其中 g ( x ) g(x) 是一个关于 x x 的多项式,包含了所有的非有理数根,剩下的部分就表示了所有的有理数根。令 g ( x ) g(x) 的常数项为 w w ,最高次项的系数为 r r ,则原方程的最高次项的系数 a n = r b i a_n=r\prod b_i ,常数项 a 0 = w c i a_0=w\prod c_i ,所以对于一个有理数解 c i b i \frac{-c_i}{b_i} c i c_i a 0 a_0 的因子, b i b_i a n a_n 的因子。

两两枚举因子,判断是否合法就行,注意还要枚举负因子。

注意,如果 a 0 = 0 a_0=0 那么还有一个解为 x = 0 x=0 ,由于系数可能为 0 0 ,所以我们需要人为的把系数不为 0 0 的最低次项作为方程的首项,最高次项作为方程的末项,再用上面的枚举因子的方法做。

猜你喜欢

转载自blog.csdn.net/VictoryCzt/article/details/83419276