快排
- 快排:先排序再递归
- 具体做法,令i=l-1,j=r+1,m=l+r>>1
- 当i<j执行以下操作,增加i直至找到比m大的数,减少j直至找到比m小的数,若i<j,交换两个数
- 递归左半边qs(l, j),递归右半边qs(j + 1, r)
/**
* 快排:先排序再递归
* 具体做法,令i=l-1,j=r+1,m=l+r>>1
* 当i<j执行以下操作,增加i直至找到比m大的数,减少j直至找到比m小的数,若i<j,交换两个数
* 递归左半边qs(l, j),递归右半边qs(j + 1, r)
**/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int a[N];
void quickSort(int q[], int l, int r){
if(l >= r) return;
int i = l - 1, j = r + 1, m = q[l + r >> 1];
while (i < j){
do ++i; while(a[i] < m);
do --j; while(a[j] > m);
if(i < j) swap(a[i], a[j]);
}
quickSort(q, l, j);
quickSort(q, j + 1, r);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
quickSort(a, 0, n - 1);
for (int i = 0; i < n; i ++ ) printf("%d ", a[i]);
}
归并
- 归并排序:先递归,再排序
- 具体做法:令 m = l + r >> 1, i = l, j = m + 1
- 递归左半边ms(l, m), 递归右半边ms(m + 1, r)
- 这是左半边是有序的,右半边也是有序的,需要一个额外数组来将这两个有序的数组合并
- 合并后归并排序完成
/**
* 归并排序:先递归,再排序
* 具体做法:令 m = l + r >> 1, i = l, j = m + 1
* 递归左半边ms(l, m), 递归右半边ms(m + 1, r)
* 这是左半边是有序的,右半边也是有序的,需要一个额外数组来将这两个有序的数组合并
* 合并后归并排序完成
**/
#include <iostream>
#include <cstring>
#include <algorithm>
const int N = 1e5 + 10;
int a[N], b[N];
void ms(int l, int r){
if(l >= r) return;
int m = l + r >> 1, i = l, j = m + 1, k = 0;
ms(l, m);
ms(m + 1, r);
while (i <= m && j <= r){
if(a[i] < a[j]) b[k++] = a[i++];
else b[k++] = a[j++];
}
while (i <= m) b[k ++] = a[i ++];
while (j <= r) b[k ++] = a[j ++];
for (int i = l, j = 0; i <= r; i ++ , j++) a[i] = b[j];
}
int main(){
int n;
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
ms(0, n - 1);
for (int i = 0; i < n; i ++ ) printf("%d ", a[i]);
}
逆序对
/**
* 逆序对:i < j且a[i] > a[j]
* 在合并时,若左半的数a[i]大于等于右半的数a[j], 那么左半边与a[j]构成逆序堆的数有 m - i + 1个
* 因为a[i] > a[j], 所以a[i + 1 ~ m]都大于a[j]
* 最终结果是左半边的逆序对+处于中间的逆序对+右半的逆序对
**/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int a[N], b[N];
LL ms(int l, int r){
if(l >= r) return 0;
int m = l + r >> 1, i = l, j = m + 1, k = 0;
LL res = ms(l, m) + ms(m + 1, r);
while (i <= m && j <= r){
if(a[i] <= a[j]) b[k++] = a[i++];
else {
b[k++] = a[j++];
res += m - i + 1;
}
}
while (i <= m) b[k++] = a[i++];
while (j <= r) b[k++] = a[j++];
for (int i = l, j = 0; i <= r; i ++, j++ ) a[i] = b[j];
return res;
}
int main(){
int n;
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
printf("%lld", ms(0, n - 1));
}
二分
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int a[N];
//找相同靠左的元素
int fl(int a[], int l, int r, int k){
while(l < r) {
int mid = l + r >> 1;
if(a[mid] >= k) r = mid; else l = mid + 1;
}
return l;
}
//找相同靠右的元素
int fr(int a[], int l, int r, int k){
while(l < r){
int mid = (l + r + 1) >> 1;
if(a[mid] <= k) l = mid; else r = mid - 1;
}
return l;
}
int main(){
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
for (int i = 0; i < m; i ++ ){
int k;
scanf("%d", &k);
int l = fl(a, 0, n - 1, k), r = fr(a, 0, n - 1, k);
if(a[l] != k || a[r] != k) printf("%d %d\n", -1, -1);
else printf("%d %d\n", l, r);
}
}
前缀和
- 构建:当前数加前一个数
- 还原:差分
- s u m [ l , r ] = s u m [ r ] − s u m [ l − 1 ] sum[l,r] = sum[r] - sum[l-1] sum[l,r]=sum[r]−sum[l−1]
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int sum[N];
int main(){
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) {
scanf("%d", &sum[i]);
sum[i] += sum[i - 1];
}
for (int i = 1; i <= m; i ++ ){
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", sum[r] - sum[l - 1]);
}
}
二维前缀和(容斥原理)
//容斥原理,减去加多的,加上减多的
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int sum[N][N];
int main(){
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <= m; j ++ ){
int t;
scanf("%d", &t);
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + t;
}
}
for (int i = 1; i <= q; i ++ ){
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 -1] + sum[x1 - 1][y1 - 1]);
}
}
差分
- 构建:后一个数减去前一个数
- [ l , r ] + c = > d [ l ] + = c , d [ r + 1 ] − = c [l,r] + c => d[l] += c, d[r + 1] -= c [l,r]+c=>d[l]+=c,d[r+1]−=c
- 还原:当前数加前一个数(前缀和)
/**
* 后一个数减去前一个数
* [l,r]+c => d[l] += c, d[r + 1] -= c
**/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int d[N];
int main(){
int n, m;
scanf("%d%d", &n, &m);
scanf("%d", &d[1]);
int pre = d[1];
for (int i = 2; i <= n; i ++ ){
scanf("%d", &d[i]);
int t = d[i];
d[i] -= pre;
pre = t;
}
for (int i = 1; i <= m; i ++ ){
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
d[l] += c;
d[r + 1] -= c;
}
for (int i = 1; i <= n; i ++ ){
d[i] += d[i - 1];
printf("%d ", d[i]);
}
}
二维差分
acwing798
a数组是b数组的前缀和,b数组是a数组的差分
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e3 + 10;
int a[N][N], b[N][N];
void insert(int x1, int y1, int x2, int y2, int c){
b[x1][y1] += c;
b[x2 + 1][y1] -= c;
b[x1][y2 + 1] -= c;
b[x2 + 1][y2 + 1] += c;
}
int main(){
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++) scanf("%d", &a[i][j]);
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++) insert(i, j, i, j, a[i][j]);
}
while (q--){
int x1, y1, x2, y2, c;
scanf("%d%d%d%d%d", &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++){
b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1]; //二维前缀和
}
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
printf("%d ", b[i][j]);
}
printf("\n");
}
}
最长不重复子序列
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
//st数组是当前区间[j, i]的元素出现的次数
int a[N], st[N];
int main(){
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
int res = 0;
for (int i = 1, j = 1; i <= n; i ++ ){
//出现a[i]次数加1
st[a[i]] += 1;
//若次数大于1,则右移直到a[i]出席的次数等于1
while(st[a[i]] > 1){
st[a[j]] --;
j++;
}
res = max(res, i - j + 1);
}
printf("%d", res);
}
数组元素的目标和
枚举一个数组,二分另一个数组即可
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int n, m, x;
int get(int d){
int l = 0, r = m - 1;
while (l < r){
int mid = l + r >> 1;
if(b[mid] >= d) r = mid; else l = mid + 1;
}
return l;
}
int main(){
scanf("%d%d%d", &n, &m, &x);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
for (int i = 0; i < m; i ++ ) scanf("%d", &b[i]);
for (int i = 0; i < n; i ++ ){
int d = x - a[i];
int idx = get(d);
if(b[idx] == d) {
printf("%d %d", i, idx);
break;
}
}
}
判断子序列
同样,枚举一个数组,遍历里一个数组
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int n, m;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= m; i ++ ) scanf("%d", &b[i]);
int cnt = 0;
for (int i = 1, j = 1; i <= n && j <= m; i ++ ){
while(j <= m && b[j] != a[i]) j++;
if(j <= m && a[i] == b[j]) {
cnt ++;
j++;
}else break;
}
if(cnt == n) printf("Yes");
else printf("No");
}
二进制中1的个数
这题没什么好说的
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int main(){
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ){
int t;
scanf("%d", &t);
int cnt = 0;
while(t > 0){
cnt += t & 1;
t >>= 1;
}
printf("%d ", cnt);
}
}
区间和
离散化+前缀和
//离散化 + 前缀和
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 3e5 + 10;
int num[N], k = 0;
PII op[N], q[N];
int a[N];
int n, m;
int get(int x){
int l = 0, r = k;
while (l < r){
int mid = l + r >> 1;
if(num[mid] >= x) r = mid; else l = mid + 1;
}
return l;
}
int main(){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ){
int x, c;
scanf("%d%d", &x, &c);
op[i] = {x, c};
num[++k] = x;
}
for (int i = 1; i <= m; i ++ ){
int l, r;
scanf("%d%d", &l, &r);
q[i] = {l, r};
num[++k] = l;
num[++k] = r;
}
//排序
sort(num + 1, num + k + 1);
//去重
k = unique(num + 1, num + k + 1) - (num + 1);
for (int i = 1; i <= n; i ++ ) {
a[get(op[i].first)] += op[i].second;
}
//求前缀和
for (int i = 1; i <= k; i ++ ) a[i] += a[i - 1];
for (int i = 1; i <= m; i ++ ) printf("%d\n", a[get(q[i].second)] - a[get(q[i].first) - 1]);
}
区间合并
- 按左端点排序
- i, j是当前区间的左右端点,cnt是区间个数
- 枚举每个区间z,有三种情况:
- 1.完全包含,跳过
- 2.部分包含,j = z.j
- 3.完全不包含 cnt += 1, i = z.i, j = z.j
/**
* 按左端点排序
* i, j是当前区间的左右端点,cnt是区间个数
* 枚举每个区间z,有三种情况:
* 1.完全包含,跳过
* 2.部分包含,j = z.j
* 3.完全不包含 cnt += 1, i = z.i, j = z.j
*
**/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
typedef pair<int, int> PII;
PII p[N];
int cmp(PII p1, PII p2){
return p1.first < p2.first;
}
int main(){
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d%d", &p[i].first, &p[i].second);
sort(p + 1, p + n + 1, cmp);
int cnt = 1;
int i = p[1].first, j = p[1].second;
for (int k = 2; k <= n; k ++ ){
int zi = p[k].first, zj = p[k].second;
//情况1
if(zi >= i && zj <= j) continue;
//情况3
else if(zi > j) {
cnt += 1;
i = zi;
j = zj;
}else {
//情况2
j = zj;
}
}
printf("%d", cnt);
}