1. 程式人生 > >[BZOJ2742][HEOI2012]Akai的數學作業[推導]

[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;
}