1.并查集
具体介绍:
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 101;
int pre[N], a[N], lev[N];
int n;
void Init()
{
for (int i = 0; i < n; i++) pre[i] = i, lev[i] = 1;
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
}
int Find(int x)
{
if (x != pre[x]) return pre[x] = Find(pre[x]);
return pre[x];
}
void Union(int a, int b)
{
if (lev[a] <= lev[b]) pre[a] = b;
else pre[b] = a;
}
int main()
{
cin >> n;
Init();
}
2.快速幂
用途:用于快速的求某数的幂
原理分析:
快速幂,龟速乘和快速乘_海龟编辑器a的b次方怎么求-CSDN博客
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL a, b;
LL f(LL a, LL b)
{
LL res = 1;
while (b)
{
if (b & 1) res *= a;
b >>= 1;
a *= a;
}
return res;
}
int main()
{
cin >> a >> b;
cout << f(a, b);
}
3.龟速乘
用途:可以慢慢的求值与值的乘积,防止爆变量的最大长度(比如爆int)
原理分析:
快速幂,龟速乘和快速乘_海龟编辑器a的b次方怎么求-CSDN博客
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL a, b;
LL f(LL a, LL b)
{
LL res = 0;
while (b)
{
if (b & 1) res += a;
b >>= 1;
a += a;
}
return res;
}
int main()
{
cin >> a >> b;
cout << f(a, b);
}
4.快速幂取模
用途:在快速幂上进行取模操作
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL a, b, mod;
LL f(LL a, LL b, int mod)
{
LL res = 1;
while (b)
{
if (b & 1) res = (res * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return res;
}
int main()
{
cin >> a >> b >> mod;
cout << f(a, b, mod);
}
5.龟速乘取模
用途:在龟速乘上进行取模操作
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL a, b, mod;
LL f(LL a, LL b, int mod)
{
LL res = 0;
while (b)
{
if (b & 1) res = (res + a) % mod;
b >>= 1;
a = (a + a) % mod;
}
return res;
}
int main()
{
cin >> a >> b >> mod;
cout << f(a, b, mod);
}
6.快速幂取模优化(结合龟速乘取模)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL a, b, mod;
LL f1(LL a, LL b, int mod)
{
LL res = 0;
while (b)
{
if (b & 1) res = (res + a) % mod;
b >>= 1;
a = (a + a) % mod;
}
return res;
}
LL f2(LL a, LL b, int mod)
{
LL res = 1;
while (b)
{
if (b & 1) res = f1(a, b, mod) % mod;
b >>= 1;
a = f1(a, a, mod) % mod;
}
return res;
}
int main()
{
cin >> a >> b >> mod;
cout << f2(a, b, mod);
}
7.矩阵快速幂
就是把快速幂中的数字换成了矩阵
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAX 100
struct Martix
{
int row; // 行
int col; // 列
int martix[MAX][MAX];
Martix(int r, int c) : row(r), col(c) {} // 构造函数
};
Martix Multiply(Martix x, Martix y)//求两个矩阵的乘积
{
Martix z(x.row, y.col);
for (int i = 0; i < z.row; i++)
{
for (int j = 0; j < z.col; j++)
{
z.martix[i][j] = 0;
for (int k = 0; k < x.col; k++)
z.martix[i][j] += x.martix[i][k] * y.martix[k][j];
}
}
return z;
}
Martix Power(Martix x, int k)
{
Martix r(x.row, x.col);
// 单位矩阵构建
for (int i = 0; i < r.row; i++)
{
for (int j = 0; j < r.col; j++)
{
if (i == j) r.martix[i][j] = 1;
else r.martix[i][j] = 0;
}
}
// 矩阵快速幂
while (k != 0)
{
if (k & 1)
r = Multiply(x, r);
k = k >> 1;
x = Multiply(x, x);
}
return r;
}
int main()
{
int r, k;
printf("请输入行(列):\n");
scanf("%d", &r);
Martix x(r, r);
printf("请输入%d行%d列的矩阵:\n", r, r);
for (int i = 0; i < x.row; i++)
for (int j = 0; j < x.col; j++)
scanf("%d", &x.martix[i][j]);
printf("请输入指数k:\n");
scanf("%d", &k);
Martix result = Power(x, k);
printf("结果是:\n");
for (int i = 0; i < result.row; i++)
{
for (int j = 0; j < result.col; j++)
printf("%d ", result.martix[i][j]);
printf("\n");
}
}
8.一维前缀和
用途:方便对一位数组进行区间处理
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int s[N], n, temp;//s[N]是前缀和数组
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
scanf("%d", &temp);
s[i] = s[i - 1] + temp;//求一维前缀和
}
for (int i = 1; i <= n; i++) printf("%d ", s[i] - s[i - 1]);//可以原来求原数组每一项
}
9.二维前缀和
用途:同一维数组
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e3 + 10;
int s[N][N], n, m ,temp;//s[N]是前缀和数组
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
scanf("%d", &temp);
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + temp;//求二维前缀和
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
printf("%d ", s[i][j] - s[i - 1][j] - s[i][j - 1] + s[i - 1][j - 1]);//也可以取原二维数组的每一项
}
cout << endl;
}
}
10.树状数组
用途:同前缀和,但是搜索时更快
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e3 + 10;
int tree[N], n, temp;
int Lowbit(int x)//求出下标位x的项维护了多长的序列,比如下标为12=0b1100,二进制中存在2个0,那么它维护的序列长度就是2的2次方,即4这么长
//也即求出下标为x的元素在树的第几层且距离其父/子节点的距离是多少
//比如x=12(0b1100),那么return 4(12的二进制后面有两个0,即处在树的第二层,所以返回二进制的100即返回4)
{
return x & -x;
}
void Add(int x, int y)
//对树状数组进行修改操作,主要是修改下标为x的元素和其父节点(还有父父……节点)的元素
{
for (int i = x; i <= n; i += Lowbit(i)) tree[i] += y;
}
int Sum(int x)
//求前缀和
{
int res = 0;
for (int i = x; i > 0; i -= Lowbit(i)) res += tree[i];
return res;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
scanf("%d", &temp);
Add(i, temp);//初始化树状树状
}
for (int i = 1; i <= n; i++) printf("%d ", Sum(i) - Sum(i - 1));//可以求出来每一项的值
}
11.线段树
用途:同前缀和,但是搜索时更快
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 1e8
const int N = 1e3 + 10;
struct Tree
{
int l, r;
int sum = 0;
}tr[N];
int arr[N], n, temp;
void Push_up(int u)//用于通过树的叶节点为上层的节点赋值和更新值
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void Build(int u, int l, int r)//创建线段树,u是当前节点下标,l,r是当前节点的左右端点
{
if (l == r) tr[u] = { l,r,arr[l] };//如果l==r,说明已经递归到叶节点,可以开始赋值
else
{
tr[u] = { l,r };//不是叶节点的节点,先赋值l,r,而sum需要通过Push_up()来完成
int mid = l + r >> 1;
Build(u << 1, l, mid);
Build(u << 1 | 1, mid + 1, r);
Push_up(u);//给非叶节点赋值sum
}
}
int GetSum(int u, int l, int r)//获得某一段的数组和,u是当前节点下标,l,r是所需要的一段的左右端点
{
if (l <= tr[u].l && r >= tr[u].r) return tr[u].sum;//如果此时需要的这一段的左右端点已经包含当前这一段,那么返回这一段的sum
else//否则不断查找,直到把所有的所需要的这一段的值全部获得
{
int sum = 0;
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) sum += GetSum(u << 1, l, r);
if (r > mid) sum += GetSum(u << 1 | 1, l, r);
return sum;
}
}
void Add(int u, int x, int y)//更新元素,u是当前节点下标,x是目标节点,y是需要加上的值
{
if (tr[u].l == tr[u].r) tr[u].sum += y;//先对叶节点进行更新,然后才能更新该叶节点的各级父节点
else
{
int mid = tr[u].l + tr[u].r >> 1;//查找下标为x的叶节点
if (x <= mid) Add(u << 1, x, y);
else if (x > mid)Add(u << 1 | 1, x, y);
Push_up(u);//逐级更新父节点
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) scanf("%d", &arr[i]);
Build(1, 1, n);
for (int i = 1; i <= n; i++) printf("%d ", GetSum(1,i,i));//输出原数组的每一项
}