hdu 1573(数论:中国剩余定理)

来源:互联网 发布:忽略此网络后怎么恢复 编辑:程序博客网 时间:2024/06/05 07:27

网上都说这个题用中国剩余定理做,但是中国剩余定理的适用条件是:

求解x=a[i](mod n[i])方程组,且n[i]两两互质

但可以发现这个题中n[i]并不两两互质

想不出解法参考网上的代码发现好多人都是用暴力写的,偏偏说是中国剩余定理鄙视

我就对着用暴力写了一个,但是暴力的过程需要优化处理,否则会超时

另外从这道题中也学会了怎么求解多个数的最小公倍数

1、求解最小公倍数(lcm)方法:

之前做题求两个数的lcm时,都是用其乘积除以它们的gcd

于是这次就很自然的这么做,才发现这样做的方法错得很离谱

比如:求2 3 4的lcm 答案很明显是12

但是用上述方法求得出来的结果是1

所以正确的方法应该是每次求出当前结果与下一个数的lcm

如果想要一次求出所有数的lcm,正确的做法如下:

如求a[1-n]的lcm

令M=a[i]*...*a[n];

令b[i]=M/a[i]; b_gcd为b数组的gcd

则a数组的lcm为M/b_gcd

用第一种方式写出来的代码会更简洁些:

for(int i=0; i<num; ++i) {            scanf("%d", &a[i]);            lcm = lcm*a[i]/gcd(lcm, a[i]);}

2、中国剩余定理

发现网上很多人说中国剩余定理的变形可以用来解决n[i]两两不互质的情况

因为感觉太繁琐了,又是模板型,就没有看,下面把普通的中国剩余定理代码贴上

#include <cmath>#include <cstdio>#include <iostream>#include <algorithm>#define MAXN 10010#define LL long longusing namespace std;int a[MAXN], n[MAXN], m[MAXN];int gcd(int a, int b) {    return b==0 ? a : gcd(b, a%b);}int expand_gcd(int a, int b, int d, int &x, int &y) {//用于求ax+by=d的一个解    if(b == 0) {        d = a;        x = 1;        y = 0;    }    else {        expand_gcd(b, a%b, d, y, x);        y -= x*(a/b);    }}int inv(int a, int n) {//用于求逆元    int d, x, y;    expand_gcd(a, n, d, x, y);    return d==1 ? (x+n)%n : -1;}int chinese_remainder(int a[], int n[], int num) {    int M = 1;    int d, x, y;    for(int i=1; i<=num; ++i) {        M *= n[i];    }    int sum = 0;    for(int i=1; i<=num; ++i) {        m[i] = M/n[i];        expand_gcd(m[i], -n[i], d, x, y);        x = inv(m[i], -n[i]);        sum += (a[i]*x*m[i])%M;    }    return sum % M;}int main(void) {    int num;    while(scanf("%d", &num) != EOF) {            for(int i=1; i<=num; ++i) {            scanf("%d %d", &a[i], &n[i]);        }                cout << chinese_remainder(a, n, num) << endl;    }    return 0;}

看到了一个很好的中国剩余定理对应n[i]非两两互质的说明

把图片传在这里


3、这道题的代码:

找到第一个i满足模方程组后,可知i+k*lcm均满足条件(k为非负数)

从这个形式可以发现第一个满足条件的一定是i 且i<=lcm

故在找i的for循环中可以限定条件i<=lcm,这样会大大优化程序运行时间

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define MAXN 10#define LL long longusing namespace std;int a[MAXN], b[MAXN];int gcd(int a, int b) {    return b==0 ? a : gcd(b,a%b);}int main(void) {    int T;    int lcm;    bool flag;    scanf("%d", &T);    while(T--) {        int max, num;        scanf("%d%d", &max, &num);        lcm = 1;        flag = false;        for(int i=0; i<num; ++i) {            scanf("%d", &a[i]);            lcm = lcm*a[i]/gcd(lcm, a[i]);        }        for(int i=0; i<num; ++i) {            scanf("%d", &b[i]);        }        int i = 0, j;        for(i=1; i<=max && i<=lcm; ++i) {            for(j=0; j<num; ++j) {                if(i%a[j] != b[j]) {                    break;                }            }            if(j == num) {                flag = true;                break;            }        }        if(!flag)            cout << "0" << endl;        else {            cout << (max-i)/lcm+1 << endl;        }    }    return 0;}


0 0
原创粉丝点击