文章目录


快速排序

快排

基本思想

1)基础算法_基础算法

#include<iostream>

using namespace std;

const int N = 1000010;

int q[N];

void quick_sort(int q[], int l, int r){

if(l >= r) return;

int x = q[l + r >> 1], i = l - 1, j = r + 1;

while(i < j){

do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i], q[j]);
}

quick_sort(q, l, j);
quick_sort(q, j+1, r);
}

int main(){

int n;

scanf("%d", &n);

for(int i = 0; i < n; i++) scanf("%d", &q[i]);

quick_sort(q, 0, n - 1);

for(int i = 0; i < n; i++) printf("%d ", q[i]);

return 0;
}

求第k小的数

#include<iostream>

using namespace std;

const int N = 100010;

int n, k, q[N];

int quick_sort(int l, int r, int k){

if(l >= r) return q[l];

int i = l - 1, j = r + 1, x = q[l + r >> 1];

while(i < j){

do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i], q[j]);
}

int sl = j - l + 1;
if(k <= sl) return quick_sort(l, j, k);

return quick_sort(j + 1, r, k - sl);
}

int main(){

scanf("%d%d", &n, &k);

for(int i = 0; i < n; i++) scanf("%d", &q[i]);

cout << quick_sort(0, n - 1, k);

return 0;
}

归并排序

1)基础算法_ios_02

归并排序

#include<iostream>

using namespace std;

const int N = 1e5 + 10;

int q[N], temp[N];

void merge_sort(int q[], int l, int r){

if(l >= r) return;

int mid = (l + r) >> 1;

merge_sort(q, l, mid), merge_sort(q, mid + 1, r);

int k = 0, i = l, j = mid + 1;

while(i <= mid && j <= r){

if(q[i] <= q[j]) temp[k++] = q[i++];
else temp[k++] = q[j++];
}

while(i <= mid) temp[k++] = q[i++];
while(j <= r) temp[k++] = q[j++];

for(i = l,k = 0; i <= r; i++, k++) q[i] = temp[k];
}

int main(){

int n;

scanf("%d", &n);

for(int i = 0; i < n; i++) scanf("%d", &q[i]);

merge_sort(q, 0, n - 1);

for(int i = 0; i < n; i++) printf("%d ", q[i]);

return 0;
}

求逆序对的个数

#include<iostream>

using namespace std;

typedef long long LL;

const int N = 1e5 + 10;

int q[N], tmp[N];

LL merge_sort(int q[], int l, int r){

if(l >= r) return 0;

int mid = l + r >> 1;

LL res = 0;
res += (merge_sort(q, l, mid) + merge_sort(q, mid + 1, r));

int k = 0, i = l, j = mid + 1;

while(i <= mid && j <= r){

if(q[i] <= q[j]) tmp[k++] = q[i++];
else{

tmp[k++] = q[j++];
res += (mid - i + 1); // 此时前半段剩下的数都严格大于当前q[j]
}
}

while(i <= mid) tmp[k++] = q[i++];
while(j <= r) tmp[k++] = q[j++];

for(i = l, k = 0; i <= r; i++, k++) q[i] = tmp[k];

return res;
}

int main(){

int n;
scanf("%d", &n);

for(int i = 0; i < n; i++) scanf("%d", &q[i]);

cout << merge_sort(q, 0, n-1) << endl;

return 0;
}

整数二分

1)基础算法_#include_03
根据分界点是红色这边还是绿色这边分为两个板子
1是红色,2是绿色

板子1

int l = 0, r = n - 1;
while(l < r){
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}

板子2

int l = 0, r = n - 1;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) l = mid + 1;
else r = mid;
}

浮点数二分

精度为4位小数的时候 1)基础算法_基础算法_04 < 1e-6,5位的时候 1)基础算法_基础算法_04 < 1e-7,6位时,1)基础算法_i++_06,相当于精度比题目要求多 1e-2 就基本不会出问题!

第一种利用 1)基础算法_#include_07
第二种直接 1)基础算法_i++_08 循环迭代 1)基础算法_基础算法_09

拿求 1)基础算法_#include_10 的三次方跟举例:

double l = -100, r = 100;

while(r - l > 1e-8){
double mid = (l + r) / 2;
if(mid * mid * mid < x) l = mid;
else r = mid;
}

高精度

高精度加法

#include<iostream>
#include<vector>

using namespace std;

vector<int> add(vector<int> &A, vector<int> &B){

vector<int> C;
int t = 0; // 进位
for(int i = 0; i < A.size() || i < B.size(); i++){

if(i < A.size()) t += A[i];
if(i < B.size()) t += B[i];

C.push_back(t % 10);
t /= 10;
}

if(t != 0) C.push_back(t);

return C;
}

int main(){

string a, b;
vector<int> A, B;

cin >> a >> b;
for(int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
for(int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');

auto C = add(A, B);

for(int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);


return 0;
}

高精度加法压位(压9位)

#include<iostream>
#include<vector>

using namespace std;

const int base = 1000000000;

vector<int> add(vector<int> &A, vector<int> &B){

vector<int> C;
int t = 0;
for(int i = 0; i < A.size() || i < B.size(); i++){

if(i < A.size()) t += A[i];
if(i < B.size()) t += B[i];

C.push_back(t % base);
t /= base;
}

if(t) C.push_back(t);

return C;
}

int main(){

string a, b;
vector<int> A, B;

cin >> a >> b;
for(int i = a.size() - 1, j = 0, t = 1, s = 0; i >= 0; i--) {

s += (a[i] - '0') * t;
j++;
t *= 10;

if(i == 0 || j == 9){
A.push_back(s);
j = 0;
s = 0;
t = 1;
}

}
for(int i = b.size() - 1, j = 0, t = 1, s = 0; i >= 0; i--) {

s += (b[i] - '0') * t;
j++;
t *= 10;

if(i == 0 || j == 9){
B.push_back(s);
j = 0;
s = 0;
t = 1;
}
}

auto C = add(A, B);

printf("%d", C.back());
for(int i = C.size() - 2; i >= 0; i--) printf("%09d", C[i]);


return 0;
}

高精度减法

#include<iostream>
#include<vector>

using namespace std;

bool cmp(vector<int> &A, vector<int> &B){

if(A.size() != B.size()) return A.size() > B.size();

for(int i = A.size() - 1; i >= 0; i--){

if(A[i] != B[i]) return A[i] > B[i];
}

return true;
}

vector<int> sub(vector<int> &A, vector<int> &B){

vector<int> C;

int t = 0;
for(int i = 0; i < A.size(); i++){

t = A[i] + t;
if(i < B.size()) t -= B[i];

C.push_back((t + 10) % 10);
if(t < 0) t = -1; // 1 或者 -1 (取决于上面是+t还是-t)
else t = 0;
}

while(C.size() > 1 && C.back() == 0) C.pop_back(); // 去掉前导0

return C;
}

int main(){

string a, b; // a = "12345"
vector<int> A, B; // A = [5, 4, 3, 2, 1]

cin >> a >> b;

for(int i = a.size() - 1; i >= 0; i--) A.push_back(a[i]-'0');
for(int i = b.size() - 1; i >= 0; i--) B.push_back(b[i]-'0');

vector<int> C;

if(cmp(A, B)) C = sub(A, B);
else{
cout << "-";
C = sub(B, A);
}

for(int i = C.size() - 1; i >= 0; i--) cout << C[i];
cout << endl;

return 0;
}

高精度乘法

#include<iostream>
#include<vector>

using namespace std;

vector<int> mul(vector<int> &A, int b){

vector<int> C;

int t = 0;

for(int i = 0; i < A.size(); i++){

t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}

while(t > 0) C.push_back(t % 10), t /= 10; // 可以与上面的for循环合并

while(C.size() > 1 && C.back() == 0) C.pop_back();

return C;
}

int main(){

string a;
int b;

cin >> a >> b;

vector<int> A;
for(int i = a.size() - 1; i >= 0; i--) A.push_back(a[i]-'0');

auto C = mul(A, b);

for(int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);

return 0;
}

高精度除法

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

// A / b = C 余数为 r
vector<int> div(vector<int> &A, int b, int &r){

vector<int> C;
r = 0;

for(int i = A.size() - 1; i >= 0; i--){

r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}

reverse(C.begin(), C.end()); // 数组反转

while(C.size() > 1 && C.back() == 0) C.pop_back();

return C;
}

int main(){

string a; // '12345'
int b;

cin >> a >> b;

vector<int> A; // [5, 4, 3, 2, 1]

for(int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');

int r;

auto C = div(A, b, r);

for(int i = C.size() - 1; i >= 0; i--) cout << C[i];

cout << endl << r;

return 0;
}

前缀和

​下标从1开始​

一维前缀和

#include<iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], s[N];

int main(){

scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);

for(int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];

while(m--){

int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]);
}


return 0;
}

二维前缀和(子矩阵的和)

1)基础算法_基础算法_11

​注意: 需要把每个格子当成一个坐标点!!!​

#include<iostream>

using namespace std;

const int N = 1010;

int a[N][N], s[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++)
scanf("%d", &a[i][j]);


for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];


while(q--){

int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
int z = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
printf("%d\n", z);
}

return 0;
}

差分

一维差分

#include<iostream>

using namespace std;

const int N = 100010;

int a[N], b[N];

void insert(int l, int r, int c){
b[l] += c;
b[r+1] -= c;
}

int main(){

int n, m;
scanf("%d%d", &n, &m);

for(int i = 1; i <= n; i++) scanf("%d", &a[i]);

for(int i = 1; i <= n; i++) insert(i, i, a[i]); // 构造差分数组

while(m--){

int l, r, c;
scanf("%d%d%d", &l, &r, &c);
insert(l, r, c);
}

for(int i = 1; i <= n; i++) b[i] += b[i-1];

for(int i = 1; i <= n; i++) printf("%d ", b[i]);

return 0;
}

二维差分(差分矩阵)

#include<iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N], b[N][N];

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(){

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]);
}
puts("");
}

return 0;
}

双指针算法

  1. 先写朴素做法
  2. 根据双指针模版+本题的性质将 “1.” 改为双指针算法(看1)基础算法_#include_121)基础算法_基础算法_13之间有没有单调关系)

最长连续不重复子序列

#include<iostream>

using namespace std;

const int N = 100010;

int a[N], s[N]; // s记录每个数出现的次数

int main(){

int n;

scanf("%d", &n);

for(int i = 0; i < n; i++) scanf("%d", &a[i]);

int res = 0;
for(int i = 0, j = 0; i < n; i++){

s[a[i]]++;

while(j <= i && s[a[i]] > 1){ // 本题这里 j <= i可以不写
s[a[j]]--;
j++;
}

res = max(res, i - j + 1);
}

cout << res << endl;

return 0;
}

数组元素的目标和

#include<iostream>

using namespace std;

const int N = 1e5 + 10;

int a[N], b[N];

int main(){

int n, m, x;

scanf("%d%d%d", &n, &m, &x);

for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= m; i++) scanf("%d", &b[i]);

for(int i = 1, j = m; i <= n; i++){

while(j && a[i] + b[j] > x) j--;

if(a[i] + b[j] == x){

printf("%d %d", (i-1), (j-1));
break;
}
}

return 0;
}

位运算

1)基础算法_i++_14 的二进制表示中第 k 位是几?

n >> k & 1

​求一个十进制数的二进制可以采用循环加上述操作计算出来​

1)基础算法_基础算法_15 操作:返回 1)基础算法_#include_10 的最后一位 1)基础算法_i++_17

1)基础算法_基础算法_18 1)基础算法_ios_19 1)基础算法_ios_20

1)基础算法_i++_21 1)基础算法_ios_22 1)基础算法_#include_10 1)基础算法_i++_24 1)基础算法_ios_22 1)基础算法_#include_10 1)基础算法_#include_27 (~1)基础算法_#include_28)

在 C++ 中 1)基础算法_i++_29 表示为补码也就是 1)基础算法_i++_29 1)基础算法_ios_22 ~1)基础算法_#include_28

#include<iostream>

using namespace std;

int main(){

int n;

cin >> n;

int x, res = 0;

while(n--){

cin >> x;

res = 0;

while(x > 0) x -= (x & -x), res ++;

cout << res << ' ';
}

return 0;
}

离散化

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 300010;

int a[N], s[N];
// 记录离散化后的操作(排序+去重)
// 某操作在alls的所在位置的下标+1 即是某操作映射到原数组的位置
vector<int> alls;
// 记录所有的添加和查询操作
vector<PII> add, query;

int find(int x){

int l = 0, r = alls.size() - 1;
// 二分找出第一个大于等于x的值
while(l < r){

int mid = l + r >> 1;
if(alls[mid] <= x) r = mid;
else l = mid + 1;
}
// +1 是因为 a[] 下标从1开始。
return r + 1;
}

int main(){

int n, m;

scanf("%d%d", &n, &m);

int x, y;
for(int i = 0; i < n; i++){

scanf("%d%d", &x, &y);
add.push_back({x, y});
alls.push_back(x);
}

for(int i = 0; i < m; i++){

scanf("%d%d", &x, &y);
query.push_back({x, y});
alls.push_back(x);
alls.push_back(y);
}
// 对操作进行排序 + 去重
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());

// 遍历添加操作, 找到离散话后的位置,进行操作
for(int i = 0; i < n; i++){

x = add[i].first;
y = add[i].second;

a[find(x)] += y;
}
// 预处理前缀和
for(int i = 1; i <= alls.size(); i++){
s[i] = s[i-1] + a[i];
}


for(int i = 0; i < m; i++){

x = query[i].first;
y = query[i].second;

int l = find(x);
int r = find(y);

cout << s[r] - s[l - 1] << endl;
}


return 0;
}

区间合并

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

typedef pair<int, int> PII;

const int N = 100010;

int n;

vector<PII> segs;

void merge(vector<PII> &segs){

vector<PII> res;
// pair<a, b> 默认先以a排序,a相同时再以b排序
sort(segs.begin(), segs.end());

int st = -2e9, ed = -2e9;
for(PII seg: segs){

if(seg.first > ed){

if(st != -2e9) res.push_back({st, ed});
st = seg.first, ed = seg.second;
}else{

ed = max(ed, seg.second);
}
}

if(st != -2e9) res.push_back({st, ed});

segs = res;
}

int main(){

cin >> n;

for(int i = 0; i < n; i++){
int l, r;
cin >> l >> r;
segs.push_back({l, r});
}

merge(segs);

cout << segs.size() << endl;

return 0;
}