2021暑假算法学习笔记(基础复习)#3

2021-07-10

昨天睡了懒觉,下午拿着三脚架出去拍了组延时,摸鱼一天。

几天继续数论学习。

欧拉函数

首先分解质因子,然后运用公式算欧拉函数。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int n;

int main()
{
    cin >> n;
    while (n -- )
    {
        int x;
        cin >> x;
        
        int ans = x;
        for (int i = 2; i <= x / i; i ++ )
            if (x % i == 0)
            {
                ans = ans / i * (i - 1);
                while (x % i == 0) x /= i;
            }
        
        if (x > 1) ans = ans / x * (x - 1);
        
        cout << ans << endl;
    }
    
    return 0;
}
筛法求euler函数

欧拉定理:若a与n互质,则有a^euler[n] mod n 同余1

在线性筛的时候,求出euler

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 1000010;

int primes[N], cnt;
bool st[N];
int euler[N];
int n;

LL get_eulers(int n)
{
    euler[1] = 1;
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])
        {
            primes[cnt ++ ] = i;
            euler[i] = i - 1;
        }
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0)
            {
                euler[primes[j] * i] = euler[i] * primes[j];
                break;
            }
            euler[primes[j] * i] = euler[i] * (primes[j] - 1);
        }
    }
    
    LL ans = 0;
    for (int i = 1; i <= n; i ++ ) ans += euler[i];
    
    return ans;
}

int main()
{
    cin >> n;
    cout << get_eulers(n) << endl;
    
    return 0;
}

快速幂

O(logk)求出a^k mod p

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

int n;
int a, k, p;

int qmi(int a, int k, int p)
{
    int res = 1;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        k >>= 1;
        a = (LL)a * a % p;
    }
    
    return res;
}

int main()
{
    scanf("%d", &n);
    while (n -- )
    {
        scanf("%d%d%d", &a, &k, &p);
        printf("%d\n", qmi(a, k, p));
    }
    
    return 0;
}
快速幂求逆元

求a mod p的乘法逆元

a存在乘法逆元的充要条件是a与p互,当模数p是质数时,a的乘法逆元是a^(p - 2)

//数据保证输入的p是质数
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

int n;
int a, p;

int qmi(int a, int k, int p)
{
    int res = 1;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        k >>= 1;
        a = (LL)a * a % p;
    }
    
    return res;
}

int main()
{
    scanf("%d", &n);
    while (n -- )
    {
        scanf("%d%d", &a, &p);
        
        LL ans = qmi(a, p - 2, p);
        if (a % p) printf("%d\n", ans);
        else puts("impossible");
    }
    
    return 0;
}
扩展欧几里得算法

对于一组数a b,求出一组x y 使得ax + by = gcd(a, b)

当b == 0时候,a 和 0的最大公约数是a,此时a的系数x是1,0的系数取0是一组解。

当b != 0时候,递归到b,a % b,此时需满足的方程是:

[a / b]意思是a/b下取整

b * y + (a - [a / b] * b) * x = d

整理得到 a * x + b * (y - [a / b] * x) = d

所以要更新一下y的值

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int n;

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

int main()
{
    scanf("%d", &n);
    while (n -- )
    {
        int a, b, x, y;
        scanf("%d%d", &a, &b);
        exgcd(a, b, x, y);
        printf("%d %d\n", x, y);
    }
    
    return 0;
}
线性同余方程
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int n;

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

int main()
{
    scanf("%d", &n);
    while (n -- )
    {
        int a, b, x, y;
        scanf("%d%d", &a, &b);
        exgcd(a, b, x, y);
        printf("%d %d\n", x, y);
    }
    
    return 0;
}
练习
CodeForces-1454B

考点 哈希表

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>

using namespace std;

const int N = 200010;

int t;
int n;
int a[N];

int main()
{
    scanf("%d", &t);
    while (t -- )
    {
        int x;
        unordered_map<int, int > h, gnd;
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%d", &a[i]);
            h[a[i]] ++ ;
            gnd[a[i]] = i;
        }
        
        int ans = 2e9;
        for (auto op : h)
            if (op.second == 1) ans = min(ans, op.first);
        
        if (ans != 2e9)
            printf("%d\n", gnd[ans]);
        else puts("-1");
    }
    
    return 0;
}
CodeForces-1440C1

类似脑筋急转弯一样,我们可以知道移动三步可以改变任意一个位置的值而其他值不改变。

特判一下最后一行,最后一列,右下角的这些特殊情况。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int t;
char g[N][N];
int n, m;

void pL(int i, int j, int k)
{
    if (!k) printf("%d %d %d %d %d %d\n", i, j, i + 1, j, i, j + 1);
    else if (k == 1) printf("%d %d %d %d %d %d\n", i, j - 1, i, j, i + 1, j);
    else if (k == 2) printf("%d %d %d %d %d %d\n", i - 1, j, i, j, i, j - 1);
    else printf("%d %d %d %d %d %d\n", i - 1, j, i, j, i, j + 1);
}

int main()
{
    cin >> t;
    while (t -- )
    {
        cin >> n >> m;
        for (int i = 1; i <= n; i ++ ) cin >> g[i] + 1;
        
        int ans = 0;
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= m; j ++ )
                if (g[i][j] == '1') ans += 3;
        cout << ans << endl;
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= m; j ++ )
                if (g[i][j] == '1')
                {
                    if (i < n && j < m)
                        pL(i, j, 0), pL(i, j + 1, 1), pL(i + 1, j, 3);
                    else if (i == n && j == m)
                        pL(i, j, 2), pL(i - 1, j, 1), pL(i, j - 1, 3);
                    else if (i == n)
                        pL(i, j, 3), pL(i - 1, j, 0), pL(i, j + 1, 2);
                    else
                        pL(i, j, 1), pL(i, j - 1, 0), pL(i + 1, j, 2);
                }
    }
    
    return 0;
}
posted @ 2021-07-11 19:16  sunnyday0725  阅读(34)  评论(0编辑  收藏  举报