文章目录
#include <iostream>
using namespace std;
/*
786.求第k个数
788.逆序对的数量
790.数的三次方根
795.前缀和
796.子矩阵的和
797.差分
798.差分方程
*/
/*786.求第k个数
给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列的第k小的数是多少。
输入格式
第一行包含两个整数 n 和 k。第二行包含 n 个整数(所有整数均在1~109范围内),表示整数数列。
输出格式
输出一个整数,表示数列的第k小数。
数据范围
1≤n≤100000,1≤k≤n
输入样例:
5 3
2 4 1 5 3
输出样例:
3
快速选择算法
1.找到分界点x,q[L],q[(L + R) / 2] , q[R]
2.左边所有数left <= x,右边所有数 >= x
3.递归排序left,递归排序right
①k <= sl(S-left-length) ,递归left(第k个在左边)
②k > sl ,(递归right)
*/
const int N = 100010; //多10 比如 防止数组下标越界
int n_01,k_01;
int q_01[N];
int quick_sort(int l,int r,int k_01){
if(l >= r) return q_01[l]; //找到 快排必须写 >= 因为最终可能没有数满足
int x = q_01[l], i = l - 1,j = r + 1; //快排边界区间在两侧
while(i < j)
{
while(q_01[++i] < x);
while(q_01[--j] > x);
if(i < j) swap(q_01[i] , q_01[j]);
}
int sl = j - l + 1;
if(k_01 <= sl) return quick_sort(l,j,k_01); //第k_01个数在左边区间
return quick_sort(j + 1,r,k_01 - sl);//k_01 - sl为右区间的第几个
}
/* n_01 k_01 数组
5 3
2 4 1 5 3
*/
int test_01(){
cin >> n_01 >> k_01;
for(int i = 0;i < n_01;i++){
cin >> q_01[i];
}
cout << quick_sort(0,n_01 - 1,k_01) << endl;
return 0;
}
/*788.逆序对的数量
题目:
给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。
输入格式
第一行包含整数 n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1≤n≤100000 (极限 全部逆序 , n + n - 1 + ... + 1 = n * (n - 1) / 2 == 10^12^ / 2 > int 2^32^ -->long long)
输入样例:
6
2 3 4 5 6 1
输出样例:
5
定义:每两个数比较 , 前面的数比后面的数大 , 则为一个逆序对
归并排序:
1. [L,R] => [L,mid],[mid + 1,R]
2. 归并排序 [L,mid] 和 [mid + 1, R]
3.归并排序 ,将左右两个有序序列合并成一个有序序列
逆序对比较三种情况 :
①都在左半边 merge_sort(L,mid)
②一个左半边,一个右半边
③都在右半边 merge_sort(mid+1,r)
*/
typedef long long LL;
int n_02;
int q_02[N],tmp_02[N];
LL merge_sort(int l,int r){
if(l >= r) return 0;
int mid = l + r >> 1;
LL res = merge_sort(l,mid) + merge_sort(mid+1,r); //排序结果相加
//归并的过程
int k = 0,i = l,j = mid + 1;//两个指针 头 和 中间 -->分段比较排序 -- 小的放前面一段
while(i <= mid && j <= r){
if(q_02[i] <= q_02[j]) tmp_02[k++] = q_02[i++];
else{
tmp_02[k++] = q_02[j++];
res += mid - i + 1; //分别在左右边已经排好序的情况③,当大于1个数即比这个数后面的数都大的区间 res要 += 此情况的逆序对个数
}
//扫尾 (没遍历完的接在后面)
while(i <= mid) tmp_02[k++] = q_02[i++];
while(j <= r) tmp_02[k++] = q_02[j++];
}
//物归原主
for(int i = l, j = 0;i <= r;i++,j++){ //q_02数组的区间 [l,r] tmp_02[j]从0开始按位置放q_02[i]
q_02[i] = tmp_02[j];
}
return res;
}
int test_02(){
cin >> n_02;
for(int i = 0;i < n_02;i++){
cin >> q_02[i];
}
cout << merge_sort(0 , n_02 - 1) << endl;
return 0;
}
/*790.数的三次方根
给定一个浮点数n,求它的三次方根。
输入格式
共一行,包含一个浮点数n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留6位小数。
数据范围
-10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000
*/
int test_03(){
double x;
cin >> x;
double l = -10000,r = 10000;
while(r - l > 1e-8)
{
double mid = (l + r) / 2;
if(mid * mid * mid >= x) r = mid;
else l = mid ;
}
printf("%lf\n",l);//lf默认保留6位小数 ,也可以加 .6
return 0;
}
/*795.前缀和
输入一个长度为n的整数序列。
接下来再输入m个询问,每个询问输入一对l, r。
对于每个询问,输出原序列中从第l个数到第r个数的和。
输入格式
第一行包含两个整数n和m。
第二行包含n个整数,表示整数数列。
接下来m行,每行包含两个整数l和r,表示一个询问的区间范围。
输出格式
共m行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n,
1≤n,m≤100000,
-1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
一维前缀和:
1. Si = a1 + a2 + ... + ai
2. sum(L,R) = aL + aL+1 + aL+2 + ... + aR = S[k] - S[i-1]
1.预处理前缀和数组
2.用公式求区间和
**重要总结** :凡是数组有用到下标i-1的都从1开始定义 ! dp / hash / dfs等
*/
int a_04[N],s_04[N];
int test_04(){
int n,m;
cin >> n >> m;
for(int i = 1;i <= n;i++){
cin >> a_04[i];
}
//求前缀和数组
for(int i = 1;i <= n;i++){ //dp 等于之前的+当前值
s_04[i] = s_04[i - 1] + a_04[i];
}
while(m--)//m次询问求前缀和
{
int l,r; //输入查询区间
cin >> l >> r;
cout << s_04[r] - s_04[l - 1] << endl; //差值即可
}
return 0;
}
/*796.子矩阵的和
输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含四个整数x1, y1, x2, y2,表示一组询问。
输出格式
共q行,每行输出一个询问的结果。
数据范围
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
?1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
思路:
1.s[i,j]如何计算
2.(x1,x2),(x2,y2) 分别子矩阵为左上角和右下角坐标
3. s[x2,y2](子矩阵和) = s[x1,y1](大到边界) - s[x1 - 1,y2] - s[x2,y1 - 1] + s[x1 - 1, y1 -1]
(s[x2,y2]指x2*y2的矩阵 ,其他同理 ,**思想**:大矩阵 - 边界的矩阵(分3个部分) + 重复多减的部分)
**画图**推导dp s[i,j] = s[i-1,j] + s[i,j-1] - s[i-1,j-1] + a[i,j]
*/
#include<cstdio>
int n_05,m_05,q_05;
int a_05[1010][1010],s_05[1010][1010]; //数组 1e12 空间直接炸
int test_05(){
scanf("%d%d%d",&n_05,&m_05,&q_05);
for(int i = 1;i <= n_05;i++){
for(int j = 1;j <= m_05;j++){
scanf("%d",&a_05[i][j]);
}
}
//初始化前缀和数组
for(int i = 1;i <= n_05;i++){
for(int j = 1;j <= m_05;j++){
s_05[i][j] = s_05[i - 1][j] + s_05[i][j-1] - s_05[i - 1][j-1] + a_05[i][j];
}
}
//询问次数
while(q_05--)
{
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%d\n_05",s_05[x2][y2] - s_05[x2][y1 -1] - s_05[x1 - 1][y2] + s_05[x1 - 1][y1 - 1]); //减去边界 x1 - 1对应y2 , x2对应y1 - 1 ,再加回 记忆
}
return 0;
}
/*797.差分 (前缀和的逆运算)
输入一个长度为n的整数序列。
接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数n和m。
第二行包含n个整数,表示整数序列。
接下来m行,每行包含三个整数l,r,c,表示一个操作。
输出格式
共一行,包含n个整数,表示最终序列。
数据范围
1≤n,m≤100000,
1≤l≤r≤n,
-1000≤c≤1000,
-1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2
差分思想: 时间复杂度变成O(1)
给定 a[1],a[2],a[3],a[4] ... a[n]
**构造差分数组 b[N]** ,使得
a[i] = b[1] + b[2] + .. b[i] (**a[i]是b数组的前缀和**)
差分核心操作:将a[L~R] 全部加上C,等价于让b[L] += C ,b[R + 1] -= C; C是∑a[L~R]
1.a[1~L-1]无影响
2.a[L~R]加上了C
3.a[R+1,N]无影响 (注意是指 数组a的值是指 [ ∑b[0~R+1] , ∑b[0~N] ] 区间值无影响(包括了加减C)! )
(看做在b数组上进行了n次的插入操作)
在b数组上 加减C,b的前缀和a整体求和b来看 是抵消不变 ,但有些段受影响 (即满足1. && 2. && 3. 就可以实现差分了)
如b[R+1] -= C ,a[L~R]在[ ∑b[0~L] , ∑b[0~R] ]值加C (R+1才减C) ,前缀和a[R+1~N]包括了b[R+1]不受影响-值不变
而前缀和a[1~L-1]即不包括b[L]又不包括b[R+1] ,完全没有影响 --等效完成了前缀和数组a的差分
*/
int n ,m;
int a[N],b[N];
void insert(int l,int r,int c){
b[l] += c;
b[r + 1] -= c;
}
int test_06(){
cin >> n >> m;
for(int i = 1;i<= n;i++){
cin >> a[i];
}
for(int i = 1;i <= n;i++){
insert(i,i,a[i]);
}
while(m--){
int l,r,c;
cin >> l >> r >> c;
insert(l,r,c);
}
for(int i = 1;i <= n;i++) a[i] = a[i - 1] + b[i];
for(int i = 1;i <= n;i++) printf("%d ",a[i]);
puts("");
return 0;
}
int main() {
test_06();
return 0;
}