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