机试训练5 —— 数学问题

一、扩展欧几里得

扩展欧几里得用于求方程ax + by = gcd(a, b)的整数解。扩展欧几里得算法可以求解二元一次方程的解,同余方程的解等问题。

1. hdu 1576  A / B

    题意:求(A / B) % 9973的值,由于A的值很大,给出A % 9973的值和B值,求(A / B) % 9973的值。

    思路:由于题目保证A一定是B的倍数,设A = B * x。n = A % 9973,则有A = 9973 * k1 + n,于是有9973 * k1 + n = B * x,将该式转化为B * x - 9973 * k1 = n,使用扩展欧几里得算法求出x和k1的整数解,然后再对x取模保证其值落在0到9972之间就可以。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

int exgcd(int a, int b, int &x, int &y)
{
    if(b != 0)
    {
        int d = exgcd(b, a % b, y, x);
        y = y - (a / b) * x;
        return d;
    }
    else
    {
        x = 1;
        y = 0;
        return a;
    }
}

int main(void)
{
    int t, n, b, x, y, ans;
    scanf("%d", &t);
    for(int i = 1; i <= t; ++ i)
    {
        scanf("%d%d", &n, &b);
        int gcd = exgcd(b, -9973, x, y);
        ans = n / gcd * x;
        ans = (ans % 9973 + 9973) % 9973;
        printf("%d\n", ans);
    }
    return 0;
}

2. hdu 2669  Romantic

    题意:求a * x + b * y = 1的x最小非负整数解,如果无解输出sorry。

    思路:直接扩展欧几里得即可。

    注意:这题一开始wrong,是因为输出y值的时候应该用调整过解范围的x来计算,而不应该用扩展欧几里得得到的x计算。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

#define ll long long

ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b != 0)
    {
        ll d = exgcd(b, a % b, y, x);
        y = y - (a / b) * x;
        return d;
    }
    else
    {
        x = 1;
        y = 0;
        return a;
    }
}

int main(void)
{
    ll a, b, x, y;
    while(scanf("%lld%lld", &a, &b) != EOF)
    {
        ll gcd = exgcd(a, b, x, y);
        if(gcd != 1)
            printf("sorry\n");
        else
        {
            x = (x % b + b) % b;
            printf("%lld %lld\n", x, (1LL - a * x) / b); //这里求解y的x必须是调整过的
        }

    }
    return 0;
}

二、鸽巢原理

1. hdu 1796  How many integers can you find

    题意:给出整数n,和因数集合M,求小于n的数里面能被M中数的某一个整除的数有几个。

    思路:求的是n以内能够被某些因数整除的数有多少个。使用鸽巢原理,即被其中1个数整除的数的数量之和减掉被其中任两个数的最小公倍数整除的数的数量之和加上被其中任三个数的最小公倍数整除的数的数量之和,以此类推。这里使用二进制枚举枚举是否被集合M中某个因数整除的所有情况。

    注意:记录因数的数组a要开成long long,否则中间计算最大公因数的时候可能出现错误,一开始a数组用的是int,wrong了。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

#define ll long long

ll a[15];

ll gcd(ll a, ll b)
{
    if(b == 0)
        return a;
    return gcd(b, a % b);
}

int main(void)
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        int np = 0;
        ll tmp;
        for(int i = 1; i <= m; ++ i)
        {
            scanf("%lld", &tmp);
            if(tmp != 0)
                a[np ++] = tmp;
        }
        ll ans = 0;
        for(int i = 1; i < (1 << np); ++ i)
        {
            int g = 1, cnt = 0;
            for(int j = 0; j < np; ++ j)
            {
                if(i & (1 << j))
                {
                    cnt ++;
                    g = g * a[j] / gcd(g, a[j]);
                }
            }
            if(cnt % 2 == 1)
                ans += (n - 1) / g;
            else
                ans -= (n - 1) / g;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33872397/article/details/82385843