1.归并排序
int q[maxn], w[maxn];//q为原数组 w为临时数组 void merge_sort(int l, int r) {//归并的左右界限 if (l >= r) return;//如果只剩一个元素 返回 int mid = l + r >> 1;//否则寻找中点 merge_sort(l, mid);//递归左半部分 merge_sort(mid + 1, r);//递归右半部分 //归并过程 int i = l, j = mid + 1, k = 0;//左右两个数组 i指向左边的数组的第一个 j指向右边数组的第一个 k为了方便从0开始 while (i <= mid && j <= r) {//当i j均未结束的时候 if (q[i] < q[j]) w[k++] = q[i++];//将小的归到w数组中 else w[k++] = q[j++]; } while (i <= mid) w[k++] = q[i++];//如果i未结束 继续加入 可以简单证明一定是最大的 while (j <= r)w[k++] = q[j++];//同理 for (i = l, j = 0; i <= r; i++, j++) {//i是这一段数组(无序) j指向我们刚刚操作完毕的w数组 将w数组放入到q数组当中 保证q数组的这一部分变得有序 第二个条件可以改成j<k 一个道理 q[i] = w[j]; } }
2.前缀和和差分
“下次你路过,人间已无我,但我的国家,依然是五岳向上,一切江河依然是滚滚向东,民族的意志依然向前,向着热腾腾的太阳,跟你一样。” ----余光中
十分基础的知识了 一直没有整理一下
前缀和和差分可以说是互逆的算法
前缀和用来解决一个数组多次查询某区间值
差分用来解决多次修改后查数组
一个偏向于差一个偏向于改 两者兼顾要用树状数组
应用这两种算法时 一般数组从1开始用
一位前缀和和二维前缀和
原数组为a[i]
建立数组b[i],使得b[i]=a[1]+a[2]+...+a[i]
求和的时候用b[r]-b[l-1]快速求和
for (int i = 1; i <= n;i++){ cin >> a[i]; } for (int i = 1; i <= n;i++){ b[i] = b[i - 1] + a[i]; }
关于二维前缀和要使用容斥原理 如图所示 要求绿色格子的值 先要把红色部分的值加上 再加上橙色部分的值 此时发现加多了一块 图三标识为紫色 再减去紫色 同时加上绿色格子本身的值即可 查询的时候同理 看代码
for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cin >> a[i][j]; } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { b[i][j] = b[i][j - 1] + b[i - 1][j] - b[i - 1][j - 1] + a[i][j]; } } while (q--) //查询 { int x1, y1, x2, y2; //这里假定x1 y1为左上角 x2 y2为右下角的值 cin >> x1 >> y1 >> x2 >> y2; int ans = b[x2][y2] - b[x1 - 1][y2] - b[x2][y1 - 1] + b[x1 - 1][y1 - 1]; cout << ans << endl; }
差分
原数组是a[]
构造一个数组b[] 使得a[i]=b[1]+b[2]+...+b[i] 具体如何构造我们不关心 假设一开始a中所有元素都是0 只需要将a每个位置加上a[i]即可
建议修改写成一个函数
如果修改a[l]-a[r] 每个加上1 修改b[r+1]-=c b[l]+=c即可
void insert(int l,int r,int c){ b[l] += c; b[r + 1] -= c; } int main(){ cin >> n >> m; for (int i = 1; i <= n;i++){ cin >> a[i]; insert(i, i, a[i]);//插入原数组 } for (int i = 0; i < m;i++){//修改 int l, r, c; cin >> l >> r >> c; insert(l, r, c); } for (int i = 1; i <= n;i++){ a[i] = a[i - 1] + b[i]; cout << a[i] << ' '; } return 0; }
二维差分
注意二维差分的插入操作的写法 一个格子加了c 对应的效果是他的右下方全部加了c
void insert(int x1,int y1,int x2,int y2,int c){ b[x1][y1] += c;//左下方全部加上 b[x1][y2 + 1] -= c;//下面减去 b[x2+1][y1] -= c;//左边减去 b[x2 + 1][y2 + 1] += c;//加回重复减去的 } int main(){ ios::sync_with_stdio(0); cin >> n >> m >> q; for (int i = 1; i <= n;i++){ for (int j = 1; j <= m;j++){ cin >> a[i][j]; insert(i, j, i, j, a[i][j]); } } int x1, x2, y1, y2, c; for (int i = 0; i < q;i++){ cin >> x1 >> y1 >> x2 >> y2 >> c; insert(x1, y1, x2, y2, c); } for (int i = 1; i <= n;i++){ for (int j = 1; j <= m;j++){ a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];//求前缀和和前面一样 cout << a[i][j] << ' '; } cout << endl; } }
3.二分查找
int bs(int l, int r) { //分成[l,mid] [mid+1,r] r=mid/l=mid+1 while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } return l; } int bs(int l, int r) { //分成[l,mid-1] [mid,r] r=mid+1/l=mid while (l < r) { int mid = l + r + 1 >> 1; //注意这里的加1 if (check(mid)) l = mid; else r = mid - 1; } return l; }
4.素数筛
在O(n)的时间复杂度中求出1-n中的所有素数
素数(质数):因子(在大于1的自然数中)只有1和本身的数
普通筛法中,一个和数可能被多个素数筛掉 比如6 被2筛一次 又被3筛一次 浪费了时间复杂度 线性筛法中将合数表示成最小素数*一个数的形式
枚举到i 设p是i的最小质因子 p1<p2<...<p(全是质数) 将p1*i,p2*i,...p*i标记为合数 这样能保证一个数字只被筛掉一次
int primes[N], cnt; //primes存素数 cnt计数 bool st[N]; //st记录该数是否是素数 void get_primes(int n) { for (int i = 2; i <= n; i++) //从2开始 { if (!st[i]) primes[cnt++] = i; //如果该数是素数 那么将其加入primes数组 for (int j = 0; primes[j] * i <= n; j++)//之后筛掉倍数 { st[primes[j] * i] = true;//标记 if (i % primes[j] == 0)//已经筛到p了 break; } } }
Comments | NOTHING