排序
♥
快速排序
♥
期望时间复杂度 O(n)
♥
模板
♥
void quick_sort(int q[], int l, int r)
{
if( l >= r) return;
// x = q[l], x = q[r]也可以试试,有几率tle
//(不知道是不是我哪里抽抽写错了才会这样)
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);
}
♥
注意
当递归使用 j 时, x 一定不能取 q[r],
使用 i 时, x 一定不能取 q[l],
否则会死循环。
x = q[r];
quick_sort(q, l, i - 1);
quick_sort(q, i , r);
♥
破案了,数据加强后取区间起点或终点作为分界点会超时,将分界点换为随机值或区间中点即可。
♥
归并排序
♥
模板
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]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
}
while(i <= mid) tmp[k++] = q[i++];
while(j <= r) tmp[k++] = q[j++];
for(i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
♥
求逆序对的数量
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]) tmp[k++] = q[i++];
else
{
tmp[k++] = q[j++];
ans += mid - i + 1;
}
}
while(i <= mid) tmp[k++] = q[i++];
while(j <= r) tmp[k++] = q[j++];
for(i = l, j = 0; i <= r; i ++ , j ++ ) q[i] = tmp[j];
}
♥
二分法
♥
整数二分
♥
模板
//区间[l, r]被划分成[l, mid] 和 [mid + 1, r]时使用
//找到 第一个 满足性质的 下标。
while(l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid; //check()判断mid是否满足性质
else l = mid - 1;
}
//区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用
//找到 最后一个 满足性质的 下标。
while(l < r)
{
int mid = l + r + 1 >> 1; // +1 向上取整
if(check(mid)) l = mid;
else r = mid - 1;
}
♥
当满足条件要改变 l 时, 要让 mid 向上取整,否则会死循环。
♥
浮点数二分
♥
模板
//esp与精确度有关,若要求精确度为1e-6则需设为1e-7
while(r - l > esp)
{
double mid = (l + r)/2.0;
if(check(mid)) r = mid;
else l = mid; // 浮点数不用+1,或-1
}
♥
高精度
♥
加法
vector<int> Add(vector<int> a,vector<int> b)
{
vector<int> c;
int t = 0; // 记得初始化为 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) c.push_back(t); // 最高位进位
return c;
}
♥
减法
//♥
#include <iostream>
#include <vector>
using namespace std;
vector<int> a, b;
bool cmp(vector<int> A, vector<int> B)
{
if(A.size() != B.size()) return A.size() > B.size();
else
{
//注意此时 A 和 B 中的数是倒着存的,应从最高位开始比大小
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;
for(int i = 0, t = 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;
else t = 0;
}
//至少要保留一位数 0, 不能让 c 为空(其实也行,特判输出 0 )
while(c.size() > 1 && c.back() == 0) c.pop_back();
return c;
}
int main()
{
string 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');
vector<int> c;
if(cmp(a, b)) c = Sub(a, b);
else
{
c = Sub(b, a);
printf("-");
}
for(int i = c.size() - 1; i >= 0; i --) printf("%d",c[i]);
return 0;
}
♥
乘法
♥
(去除前导零 别写成 ‘0’ 了…)
自己手搓的两个都是大数的:
//♥
#include <iostream>
#include <vector>
using namespace std;
vector<int> a, b, c;
vector<int> Mul(vector<int> a, vector<int> b)
{
vector<int> c;
int t;
for(int i = 0; i < b.size(); i ++ )
{
t = 0;
for(int j = 0; j < a.size(); j ++ )
{
t = a[j]*b[i] + t;
if(j + i + 1 > c.size()) c.push_back(t%10);
else
{
t += c[j + i];
c[j + i] = t%10;
}
t /= 10;
}
if(t) c.push_back(t);
}
//去除前导零
while(c.size() > 1 && c.back() == 0) c.pop_back();
return c;
}
int main()
{
string 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');
c = Mul(a, b);
for(int i = c.size() - 1; i >= 0; i -- ) cout << c[i];
return 0;
}
当一个为大数,一个较小时:
//♥
#include <iostream>
#include <vector>
using namespace std;
vector<int> a, c;
vector<int> Mul(vector<int> a, int b)
{
vector<int> c;
int t = 0;
for(int i = 0; i < a.size() || t; i ++ )
{
if(i < a.size()) t += a[i]*b;
c.push_back(t%10);
t /= 10;
}
while(c.size() > 1 && c.back() == 0) c.pop_back();
return c;
}
int main()
{
string A;
int b;
cin >> A >> b;
for(int i = A.size() - 1; i >= 0; i -- ) a.push_back(A[i] - '0');
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;
vector<int> a, c;
int b, t;
vector<int> Div(vector<int> a, int b)
{
vector<int> c;
t = 0;
for(int i = 0; i < a.size(); i ++ )
{
t = t*10 + a[i];
c.push_back(t/b);
t %= b;
}
reverse(c.begin(), c.end());
while(c.size() > 1 && c.back() == 0) c.pop_back();
return c;
}
int main()
{
string A;
cin >> A >> b;
for(int i = 0; i < A.size(); i ++ ) a.push_back(A[i] - '0');
c = Div(a, b);
int flag = 0;
for(int i = c.size() - 1; i >= 0; i -- ) printf("%d",c[i]);
printf("\n%d\n",t);
return 0;
}
♥
前缀和(二维 )
//♥
#include <iostream>
using namespace std;
const int N = 1005;
int n, m, q;
int s[N][N];
int main()
{
cin >> n >> m >> q;
for(int i = 0; i <= n; i ++ ) s[i][0] = 0;
for(int i = 0; i <= m; i ++ ) s[0][i] = 0;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
cin >> s[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];
while(q -- )
{
int x1, x2, y1, y2;
scanf("%d%d%d%d",&x1, &y1, &x2, &y2);
printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
}
return 0;
}
♥
☆ 差分 (二维 )
//♥
#include <iostream>
using namespace std;
int n, m, q;
int a[1005][1005];
void Add(int x1, int y1, int x2, int y2, int c)
{
a[x1][y1] += c;
a[x2 + 1][y1] -= c;
a[x1][y2 + 1] -= c;
a[x2 + 1][y2 + 1] += c;
}
int main()
{
cin >> n >> m >> q;
for(int i = 1; i <= n; i ++ ) a[i][0] = 0;
for(int i = 1; i <= m; i ++ ) a[0][i] = 0;
int c;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
{
cin >> c;
Add(i, j, i, j, c);
}
while(q -- )
{
int x1, x2, y1, y2;
scanf("%d%d%d%d%d",&x1, &y1, &x2, &y2, &c);
Add(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];
printf("%d ",a[i][j]);
}
puts("");
}
return 0;
}
♥
双指针
♥
核心思想
将 O(n2) 的时间复杂度优化至 O(n)
//O(n^2)
for(int i = 0; i < n; i ++ )
for(int j = 0; j <= i; j ++ )
if(check(i, j))
//每道题的具体逻辑
res = max(res, j - i + 1);
//O(n) 双指针
for(int i = 0, j = 0; i < n; i ++ )
{
while(j <= i && check(i, j)) j ++ ;
//每道题的具体逻辑
res = max(res, j - i + 1);
♥
思路
先暴力,观察 i , j 是否具有单调关系 → 指j 单向移动(大概)
♥
题目链接
求最长不包含重复数的连续区间长度
//♥
#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int n;
int a[N], s[N];
int main()
{
cin >> n;
for(int i = 0; i < n; i ++ ) scanf("%d",&a[i]);
int ans = 0;
for(int i = 0, j = 0; i < n; i ++ )
{
s[a[i]] ++ ;
while(s[a[i]] > 1 && j < i) s[a[j++]] -- ;
ans = max(ans, i - j + 1);
}
cout << ans << endl;
return 0;
}
♥
位运算
优先级(我看别人说的没亲测): ~ 按位取反,& 按位与,^ 按位异或,| 按位或
1) ~ 按位取反
相当于 非
就是把 1 和 0 互换
例如 ~101 就是 010
2) & 按位与
就 相当于 且
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
3) ^ 按位异或
两边 不同 才为 真
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
4) | 按位或
这就相当于 或
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
lowbit() 用于计算最低位 1 出现的位置
int lowbit(int x)
{
return x & (-x);
// 负数的二进制就相当于 取反 +1
//例如 5 的二进制是 101
// -5 则是 (~101)+ 1 = 010 + 1 = 011
// 101 & 011 = 1 即 最低位 1 出现的位置
}
//也可以用此计算一个二进制数中 1 出现的次数
while(x)
{
x -= lowbit(x);
n++;
}
5) << 左移运算符
x << n
将一个数 (x) 的各二进制位全部左移 n 位(左边超过范围的丢弃,右边补 0 )
在不超范围的情况下,相等于乘以 2
6) >> 右移运算符
x >> n
将一个数的各个二进制位全部右移 n 位, 正数左补0, 负数左补1,右边丢弃
相当于除以 2
♥
☆离散化
♥
模板
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去重
// unique()会将所有重复元素换到末尾位置,并返回去重后最后一个元素地址
//二分求出 x 对应的离散化值
int Find(int x)
{
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, 2 , 3 ... n ;从 0 开始则不用 + 1
}
♥
题目链接
n 次操作,每次在某一位置 x 上的数 加上 c
m 次询问,求出 [l, r] 之间所有数的和
1 ≤ n, m ≤ 105
-109 ≤ l ≤ r ≤ 109
-109 ≤ x ≤ 109
♥
//♥
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PR;
const int N = 3e5 + 5;
// Q : 为啥不是 1e5 + 5?
// A : m 次询问的 l,r 也需进行离散化
int n, m, x, c;
int a[N], s[N];
vector<int> alls;
vector<PR> add, query;
int Find(int x)
{
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 开始,前缀和一般都需要从 1 开始,较方便计算
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i ++ )
{
scanf("%d%d", &x, &c);
add.push_back({x, c});
alls.push_back(x);
}
for(int i = 0; i < m; i ++ )
{
int l, r;
scanf("%d%d", &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);
printf("%d\n",s[r] - s[l - 1]);
}
return 0;
}
♥
for(auto a : b)
其中,b 为一个容器,效果是利用 a 遍历并且获取容器 b 中的每个值,且 a 的改变不会影响到 b 中的元素
for(auto &a : b)
对 a 进行改变,会影响到 b 中的元素
♥
区间合并
先对左端点进行排序 balabala…
//♥
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PR;
// pair 的 sort 默认先排第一个元素,从小到大
const int N = 1e5 + 5;
int n, res = 0;
vector<PR> a;
int main()
{
scanf("%d",&n);
while(n -- )
{
int l, r;
scanf("%d%d", &l, &r);
a.push_back({l, r});
}
sort(a.begin(), a.end());
int ed = a[0].second; // 初始化一下ed
for(auto i : a)
{
if(i.first > ed)
{
res ++ ;
ed = i.second;
}
else ed = max(ed, i.second);
}
cout << res + 1 << endl;
return 0;
}