基础课 第一讲 基础算法
作者:互联网
快速排序
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.逆序对的数量
#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
由差分矩阵得到正常矩阵:
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