[BZOJ2742][HEOI2012]Akai的数学作业(数学)

设有理数解的集合为 {p1q1,p2q2,p3q3,...,ptqt} { p 1 q 1 , p 2 q 2 , p 3 q 3 , . . . , p t q t } ,那么可以将原方程分解因式:
(q1xp1)(q2xp2)(q3xp3)...(qtxpt)ORZ ( q 1 x − p 1 ) ( q 2 x − p 2 ) ( q 3 x − p 3 ) . . . ( q t x − p t ) O R Z
ORZ O R Z 表示不可分解的因式。
ORZ O R Z 的常数项为 w w ,最高次项为r,则一定有:
原方程的最高次项 an=rti=1qi a n = r ∏ i = 1 t q i
原方程的常数项 a0=wti=1(pi) a 0 = w ∏ i = 1 t ( − p i )
由于上面涉及到的 p p q w w r等数都是整数,因此最简分数 pq p q 是原方程解的必要条件是:

p|a0 and q|an p | a 0  and  q | a n

一看系数的绝对值只有 2×107 2 × 10 7 a0 a 0 an a n 的约数不多,因此可以枚举 a0 a 0 an a n 的约数求出一个 pq p q ,并判断 x=pq x = p q 是不是原方程的解。
如何判断 x=pq x = p q 是不是原方程的解呢?将 x=pq x = p q 代入原方程得到:
i=0nai(pq)i ∑ i = 0 n a i ( p q ) i

乘以 qn q n 得到:
i=0naipiqni ∑ i = 0 n a i p i q n − i

所以 x=pq x = p q 是原方程的解的条件是:
i=0naipiqni=0 ∑ i = 0 n a i p i q n − i = 0

pi p i qni q n − i 的值都很大,使用高精度不好写又容易TLE,因此考虑用几个质数取模 (哈希大法好啊!)
当然,还要特判 a0=0 a 0 = 0 的情况:这时候方程存在一个解 x=0 x = 0 。然后从低次幂向高次幂扫描,如果扫描到 ah0 a h ≠ 0 ,就终止扫描,将 ah a h 作为方程的常数项,以此类推,将 ah+i a h + i 作为方程的 i i 次项(0<inh),构成一个一元 nh n − h 次方程。然后就和上面一样了。
注意约分,排序和去重。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 105, MX = 1e9 + 9, PYZ = 1e9 + 7, LPF = 998244353,
CYX = 666623333, CX = 1e8 + 7, CYF = 23456789, M = 1e6 + 5;
struct cyx {
    int x, y;
    cyx() {}
    cyx(int _x, int _y) : x(_x), y(_y) {}
} ans[M], R[M];
bool comp(cyx a, cyx b) {
    double x = 1.0 * a.x / a.y, y = 1.0 * b.x / b.y;
    return x < y;
}
int qpow(int a, int b, int MX) {
    a = (a % MX + MX) % MX;
    int res = 1; while (b) {
        if (b & 1) res = 1ll * res * a % MX;
        a = 1ll * a * a % MX; b >>= 1;
    }
    return res;
}
int n, b[N], a[N], tot, tot1, a1[M], tot2, a2[M], cnt;
bool check(int p, int q) {
    int i, ans = 0; for (i = 0; i <= n; i++)
        ans = (ans + 1ll * a[i] * qpow(p, i, MX) % MX
        * qpow(q, n - i, MX) % MX) % MX; if (ans) return 0;
    ans = 0; for (i = 0; i <= n; i++)
        ans = (ans + 1ll * a[i] * qpow(p, i, PYZ) % PYZ
        * qpow(q, n - i, PYZ) % PYZ) % PYZ; if (ans) return 0;
    ans = 0; for (i = 0; i <= n; i++)
        ans = (ans + 1ll * a[i] * qpow(p, i, LPF) % LPF
        * qpow(q, n - i, LPF) % LPF) % LPF; if (ans) return 0;
    ans = 0; for (i = 0; i <= n; i++)
        ans = (ans + 1ll * a[i] * qpow(p, i, CYX) % CYX
        * qpow(q, n - i, CYX) % CYX) % CYX; if (ans) return 0;
    ans = 0; for (i = 0; i <= n; i++)
        ans = (ans + 1ll * a[i] * qpow(p, i, CX) % CX
        * qpow(q, n - i, CX) % CX) % CX; if (ans) return 0;
    ans = 0; for (i = 0; i <= n; i++)
        ans = (ans + 1ll * a[i] * qpow(p, i, CYF) % CYF
        * qpow(q, n - i, CYF) % CYF) % CYF;
    return ans == 0;
}
int main() {
    int i, j, t = 0; n = read(); for (i = 0; i <= n; i++) b[i] = read();
    if (!b[t]) {while (!b[t]) t++; ans[++tot] = cyx(0, 1);}
    for (i = t; i <= n; i++) a[i - t] = b[i]; n -= t;
    int S1 = sqrt(abs(a[0])), S2 = sqrt(abs(a[n]));
    for (i = 1; i <= S1; i++) if (a[0] % i == 0) {
        a1[++tot1] = i; a1[++tot1] = -i; if (i * i != abs(a[0]))
            a1[++tot1] = a[0] / i, a1[++tot1] = -a[0] / i;
    }
    for (i = 1; i <= S2; i++) if (a[n] % i == 0) {
        a2[++tot2] = i; a2[++tot2] = -i; if (i * i != abs(a[n]))
            a2[++tot2] = a[n] / i, a2[++tot2] = -a[n] / i;
        }
    for (i = 1; i <= tot1; i++) for (j = 1; j <= tot2; j++)
        if (check(a1[i], a2[j])) ans[++tot] = cyx(a1[i], a2[j]);
    for (i = 1; i <= tot; i++) {
        int w = __gcd(ans[i].x, ans[i].y);
        ans[i].x /= w; ans[i].y /= w; if (ans[i].y < 0)
            ans[i].x = -ans[i].x, ans[i].y = -ans[i].y;
    }
    sort(ans + 1, ans + tot + 1, comp);
    for (i = 1; i <= tot; i++)
        if (i == 1 || ans[i].x != ans[i - 1].x || ans[i].y != ans[i - 1].y)
            R[++cnt] = ans[i]; cout << cnt << endl;
    for (i = 1; i <= cnt; i++)
        if (R[i].y == 1) printf("%d\n", R[i].x);
        else printf("%d/%d\n", R[i].x, R[i].y);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值