[BZOJ2742][HEOI2012]Akai的数学作业[推导]
题意
给定各项系数,求一元 \(n\) 次方程的有理数解。
\(n\leq 100\)。
分析
-
设答案为 \(\frac{p}{q}\) ,那么多项式可以写成 \(a_0\frac{p}{q}+a_1\frac{p^2}{q^2}+\cdots a_n\frac{p^n}{q^n}\) 的形式。
-
左右乘以 \(q^n\) :得到:\(\sum_{i=0}^n {a_ip^iq^{n-i}}=0\) 。
-
可以推出
- \(a_0q^n=p*A\) ;
- \(a_np^n=q*B\) ;
-
因为 \(p,q\) 互质,所以得到 \(a_0\) 必为 \(p\) 倍数, \(a_n\) 必为 \(q\) 倍数。
-
因为最低项可能为 \(0\),所以以第一个不为 \(0\) 的项作为基准,结论是一致的。
-
可以通过对几个大质数取模的方式判断是否为 \(0\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].last,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
inline int gi(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=107;
int n,mod,cnt;
int a[N],bp[N],bq[N];
int M[]={19260817,998244353,1000000007,19491001,48271};
vector<int>v0,vn;
void add(int &a,int b){a+=b;if(a>=mod) a-=mod;}
bool check(int p,int q){
for(int j=0;j<5;++j){
mod=M[j];int tmp=0;
bp[0]=bq[0]=1;
for(int i=1;i<=n;++i) bp[i]=1ll*bp[i-1]*p%mod,bq[i]=1ll*bq[i-1]*q%mod;
rep(i,0,n) add(tmp,(1ll*a[i]*bp[i]%mod*bq[n-i]%mod+mod)%mod);
if(tmp) return 0;
}
return 1;
}
int gcd(int a,int b){
return !b?a:gcd(b,a%b);
}
struct fs{
int fz,fm;
bool operator <(const fs &b)const{
return 1ll*fz*b.fm<1ll*b.fz*fm;
}
bool operator !=(const fs &b)const{
return fz!=b.fz||fm!=b.fm;
}
};
vector<fs>ans;
int main(){
n=gi();
rep(i,0,n) a[i]=gi();
int x=0;
while(!a[x]) ++x;
if(x) v0.pb(0);
x=abs(a[x]);
for(int i=1,l=(int)sqrt(x);i<=l;++i)if(x%i==0){
v0.pb(i),v0.pb(-i);
if(i*i!=x) v0.pb(x/i),v0.pb(-x/i);
}
x=abs(a[n]);
for(int i=1,l=(int)sqrt(x);i<=l;++i)if(x%i==0){
vn.pb(i);
if(i*i!=x) vn.pb(x/i);
}
for(auto p:v0)
for(auto q:vn){
int x=p,y=q;
if(check(x,y)){
int g=gcd(x,y);
x/=g,y/=g;
ans.pb((fs){x,y});
}
}
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();++i)
if(i==0||ans[i]!=ans[i-1]) ++cnt;
printf("%d\n",cnt);
for(int i=0;i<ans.size();++i)
if(i==0||ans[i]!=ans[i-1]){
if(ans[i].fm!=1) printf("%d/%d\n",ans[i].fz,ans[i].fm);
else printf("%d\n",ans[i].fz);
}
return 0;
}