编程语言
首页 > 编程语言> > 基础课 第一讲 基础算法

基础课 第一讲 基础算法

作者:互联网

快速排序

785.快速排序

排序看似简单,其实边界问题还挺麻烦

786.第k个数(快速选择\(O(n)\))

求数组中第k大的数

快速选择算法——只用递归一边的快排,复杂度 \(O(n)\)

在快速排序的某次递归中,记左区间有 \(L\) 个元素,右区间有 \(R\) 个元素。如果 \(k\le L\) 则递归左区间,找左区间中第 \(k\) 大的数。否则递归右区间,找右区间中第 \(k-L\) 大的数。

#include <bits/stdc++.h>
using namespace std;
const signed N = 1e5+10;

int a[N];

int qs(int q[], 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 L = j-l+1;
    return k <= L ? qs(q,l,j,k) : qs(q,j+1,r,k-L);
}

signed main()
{
    int n, k; scanf("%d%d", &n, &k);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    printf("%d", qs(a, 0, n - 1, k));

    return 0;
}

归并排序

787.归并排序
788.逆序对的数量

luogu P1908 逆序对

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const signed N = 5e5+10;

int a[N];

int tmp[N];
ll ms(int l, int r)
{
    if(l >= r) return 0;
    int mid = l + r >> 1;
    ll res = ms(l, mid) + ms(mid + 1, r); //核心

    int k = 0, i = l, j = mid + 1;
    while(i <= mid && j <= r)
        if (a[i] <= a[j]) tmp[k++] = a[i++];
        else tmp[k++] = a[j++], res += mid - i + 1; //核心
    while(i <= mid) tmp[k++] = a[i++];
    while(j <= r) tmp[k++] = a[j++];
    for(i = l, j = 0; i <= r; i++, j++) a[i] = tmp[j];

    return res;
}

signed main()
{
    int n; scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    printf("%lld", ms(0, n-1));

    return 0;
}

二分

789.数的范围

注意找不到的话 \(l==\) 区间右端点

790.数的三次方根

高精度

四题都锁了,不想写了

791.高精度加法
792.高精度减法
793.高精度乘法
794.高精度除法

前缀和与差分

795.前缀和
796.子矩阵的和

二维前缀和 S[i][j] = a[i][j] + S[i-1][j] + S[i][j-1] - S[i-1][j-1]

797.差分

可以把输入 a[i] 视为把区间 \([i,i]\) 中的数加上 a[i] ,从而用区间加建立差分数组

#include <bits/stdc++.h>
using namespace std;
const signed N = 1e5+10;
int b[N];
void ins(int l, int r, int x) {b[l] += x; b[r+1] -= x; }
signed main()
{
    int n, m; cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        int tmp; cin >> tmp;
        ins(i,i,tmp);
    }
    while(m--)
    {
        int l ,r, x; cin >> l >> r >> x;
        ins(l,r,x);
    }
    for(int i = 1; i <= n; i++)
    {
        b[i] += b[i - 1]; //求前缀和得到正常矩阵
        cout << b[i] << ' ';
    }

    return 0;
}

798.差分矩阵

原矩阵是差分矩阵的前缀和,某点加 c 会把这个点右下的数全加 c

对矩形 (x1,y1)(x2,y2) 中的元素加 c:

b[x1][y1]+=c, b[x2+1][y1]-=c, b[x1][y2+1]-=c, b[x2+1][y2+1]+=c

image

由差分矩阵得到正常矩阵:

b[i][j] += b[i-1][j] + b[i][j - 1] - b[i - 1][j - 1]

双指针算法

“单调关系”

799.最长连续不重复子序列
//j在i的左边,注意在i右移的同时,j也不能左移,是为“单调关系”
for(int i = 0, j = 0; i < n; i++)
{
    cnt[a[i]]++; //字符出现次数
    while(s[a[i]] > 1) s[a[j]--], j++;
    ans = max(ans, i - j + 1);
}
800.数组元素的目标和

输出满足 a[i] + b[j] == x 的所有 a[i]b[j] 。如果解唯一就能用双指针,否则不行,要用哈希(怎么做呢)

位运算

801.二进制中1的个数
while(x) x -= lowbit(x), ans++;

离散化

802.区间和

假定有一个无限长的数轴,数轴上每个坐标上的数都是0。

现在,我们首先进行 n 次操作,每次操作将某一位置x上的数加c。

接下来,进行 m 次询问,每个询问包含两个整数l和r,你需要求出在区间[l, r]之间的所有数的和。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 300010;
int n, m;
int a[N], s[N];
vector<int> alls;
vector<PII> add, query;
int find(int x){ //此函数可用lower_bound代替
    int l = 0, r = alls.size() - 1;
    while (l < r){
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; //从1开始方便前缀和
}
int main(){
    cin >> n >> m;
    for (int i = 0; i < n; i ++ ){
        int x, c;
        cin >> x >> c;
        add.push_back({x, c});
        alls.push_back(x);
    }
 
    for (int i = 0; i < m; i ++ ){
        int l, r;
        cin >> l >> r;
        query.push_back({l, r});
        alls.push_back(l);
        alls.push_back(r);
    }
    // 去重
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(),alls.end()), alls.end());
    // 处理插入
    for (auto item : add){
        int x = find(item.first);
        a[x] += item.second;
    }
    // 预处理前缀和
    for (int i = 1; i <= alls.size(); i ++ ) s[i] = s[i - 1] + a[i];
    // 处理询问
    for (auto item : query){
        int l = find(item.first), r = find(item.second);
        cout << s[r] - s[l - 1] << endl;
    }
    return 0;
}

区间合并

803.区间合并

把有交集或端点相交的区间合并,输出合并后区间数

按左端点排序,逐个判断前后区间是否相交

sort(segs.begin(), segs.end());
int st = -2e9, ed = 2e9;
for(auto seg : segs)
    if(ed < seg.first)
    {
        if(st != 2e9) ans.push_back({st, ed});
        st = seg.first, ed = seg.second;
    }
    else ed = max(ed, seg.second);
if(st != 2e9) ans.push_back({st, ed});

标签:第一,int,ed,while,mid,算法,alls,基础课,区间
来源: https://www.cnblogs.com/wushansinger/p/15470073.html