voidquick_sort(int q[], int l, int r) { //判定边界:区间内只有一个数或没有数时不需要排序 if (l >= r) return; int x = q[(l + r) / 2], i = l - 1, j = r + 1; //取分界值 //注意:由于每次完成交换(包括初始状态)都自动向中间移动一格,初始时要将两侧指针取在区间外侧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 t = q[i]; q[i] = q[j]; q[j] = t; } } quick_sort(q, l, j); quick_sort(q, j + 1, r); }
intmain() { 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]); return0; }
voidmerge_sort(int q[], int l, int r) { //判定边界:区间内只有一个数或没有数时不需要排序 if (l >= r) return; int mid = (l + r) / 2; //取分界点:当前区间中点 merge_sort(q, l, mid); merge_sort(q, mid + 1, r); // 归并过程 int k = 0, i = l, j = mid + 1; //k:当前tmp中已有数值个数;i、j:指向左、右半边起点指针 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]; }
intmain() { 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]); return0; }
intmain() { scanf("%d", &n); for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); sort(q, q + n); for (int i = 0; i < n; i ++ ) printf("%d ", q[i]); return0; }
intmain() { scanf("%d%d", &n, &m); for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); while (m -- ) { int x; scanf("%d", &x); //查找左边界 int l = 0, r = n - 1; while (l < r) { int mid = (l + r) / 2; if (q[mid] >= x) //性质条件的选择:需要注意数组中可能含有多个相同数值(有序数列中连续排列) r = mid; else l = mid + 1; } //不存在该元素的判断:若数组中不存在指定的查找元素,该算法会输出数组中所有大于指定查找元素的数中的最小值,该值必然不等于指定查找元素 if (q[l] != x) cout << "-1 -1" << endl; else { cout << l << ' '; //查找右边界 int l = 0, r = n - 1; while (l < r) { int mid = (l + r + 1) / 2; if (q[mid] <= x) //性质条件的选择:需要注意数组中可能含有多个相同数值(有序数列中连续排列) l = mid; else r = mid - 1; } cout << r << endl; } } return0; }
usingnamespace std; // C = A + B 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) C.push_back(1); return C; }
intmain() { string a, b; vector<int> A, B; cin >> a >> b; // a = "123456" // 将读入的大整数(字符串形式)以各数位数字形式逆序存入数组,便于数位增减 for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0'); // A = [6, 5, 4, 3, 2, 1]; for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0'); auto C = add(A, B); //auto:让编译器自主推断数据类型 // 将相加后得到向量逆序输出 for (int i = C.size() - 1; i >=0; i -- ) cout << C[i]; return0; }
usingnamespace std; // 判断是否有 A >= B boolcmp(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]; returntrue; }
// C = A - B vector<int> sub(vector<int> &A, vector<int> &B)// 加引用是为了提高效率,不加引用会把数据先拷贝一遍 { vector<int> C; int t = 0; // 借位标志 for (int i = 0; i < A.size() || i < B.size(); i ++ ) { t = A[i] - t; if (i < B.size()) // 通过比较大小已确定A.size() >= B.size() t -= B[i]; C.push_back((t + 10) % 10); // 若t≥0,返回仍然为t;若t<0,返回t+10。这种写法是对两种情况的综合 if (t < 0) t = 1; else t = 0; } // 去除有效位前面的空余零位(即前导0) while (C.size() > 1 && C.back() == 0) // 去掉数组中(倒序存入)中从最后一个开始数(即从最高位开始)为0的全部数,直到某一处不为0时停止;C.size() > 1:整个数只有一位且为0时不去除 C.pop_back(); return C; }
intmain() { string a, b; vector<int> A, B; cin >> a >> b; // a = "123456" // 将读入的大整数(字符串形式)以各数位数字形式逆序存入数组,便于数位增减 for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0'); // A = [6, 5, 4, 3, 2, 1]; for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0'); if (cmp(A, B)) { auto C = sub(A, B); //auto:让编译器自主推断数据类型 // 将相加后得到向量逆序输出 for (int i = C.size() - 1; i >=0; i -- ) cout << C[i]; } else { auto C = sub(B, A); cout << "-"; for (int i = C.size() - 1; i >=0; i -- ) cout << C[i]; } return0; }
// C = A * b vector<int> mul(vector<int> &A, int b) { vector<int> C; int t = 0; // 借位标志 for (int i = 0; i < A.size() || t != 0; i ++ ) // 循环截止条件:大数各数位均完成相乘且进位完成 { if (i < A.size()) // 若大数各数位还未遍历相乘完成,则执行相乘并将进位项加入 t += A[i] * b; C.push_back(t % 10); t /= 10; // 进位 } // 去除有效位前面的空余零位(即前导0) while (C.size() > 1 && C.back() == 0) // 去掉数组中(倒序存入)中从最后一个开始数(即从最高位开始)为0的全部数,直到某一处不为0时停止;C.size() > 1:整个数只有一位且为0时不去除 C.pop_back(); return C; }
intmain() { string a; int b; vector<int> A; cin >> a >> b; // 将读入的大整数(字符串形式)以各数位数字形式逆序存入数组,便于数位增减 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 -- ) cout << C[i]; return0; }
// A / b, 商是C, 余数是r vector<int> div(vector<int> &A, int b, int &r)// 余数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()); //除法过程中商由高位到低位依次存入向量C,需调用头文件<algorithm>中的reverse方法对向量进行逆序处理后再去除前导0 // 去除有效位前面的空余零位(即前导0) while (C.size() > 1 && C.back() == 0) // 去掉数组中(倒序存入)中从最后一个开始数(即从最高位开始)为0的全部数,直到某一处不为0时停止;C.size() > 1:整个数只有一位且为0时不去除 C.pop_back(); return C; }
intmain() { string a; int b; vector<int> A; cin >> a >> b; // 将读入的大整数(字符串形式)以各数位数字形式逆序存入数组,便于数位增减 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 << endl; return0; }
// 插入函数insert:将数组a在[l, r]区间内的所有元素加上常数c,差分数组b产生的变化 voidinsert(int l, int r, int c) { b[l] += c; b[r + 1] -= c; }
intmain() { // 注意:下标均从1开始,因为求前缀和要求a[0]=0 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); } // 对差分数组b求其前缀和得到输出数组a(插入后) for (int i = 1; i <= n; i ++ ) b[i] += b[i - 1]; for (int i = 1; i <= n; i ++ ) printf("%d ", b[i]); return0; }
intmain() { char str[1000]; gets(str); int n = strlen(str); // 字符串长度 for (int i = 0; i < n; i ++ ) { int j = i; // 想用j从一个单词的开头一直指到该单词末尾 while (j < n && str[j] != ' ') j ++ ; // 问题具体逻辑 for (int k = i; k < j; k ++ ) cout << str[k]; cout << endl; i = j + 1; } return0; }
int res = 0; while (x != 0) { x -= lowbit(x); // 每次减去x的最后一位1 res ++ ; } cout << res << " "; } return0; }
离散化
特指整数的、保序(小的仍小、大的仍大)的离散化。
基本含义:数组的映射
e.g. 这类问题通常会研究一个有序数组a[],其中数值的值域在0~10^9,数组长度不超过10^5【数组值域跨度大,但分布稀疏】;若想统计各元素数值在数组中出现的个数,需要新开一个数组b[]用于存储,其以数组a[]中各元素数值作为下标,数组b[]在该下标处的数值即为其在数组a[]中出现的次数;但此时值域过大,由于内存限制开不了这么长的数组,因此需要将原数组映射到一个新的数组中以缩小值域范围(通常映射至其下标)
intfind(int x)// 求x值离散化后结果(映射至下标数组) { int l = 0, r = alls.size() - 1; while (l < r) { int mid = (l + r) / 2; if (alls[mid] >= x) r = mid; else l = mid + 1; } return r + 1; // 后续需要使用前缀和,故将原数组映射到以1为起点的下标数组 }
intmain() { 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; } return0; }