当前位置: 首页 > news >正文

6.3 排序、RMQ

排序

七夕祭
转换成一个环形均分纸牌的问题,以按行划分为例
每一行感兴趣的数量用a[N]a[N]a[N]表示
x1x_1x1表示a1a_1a1挪给ana_nan的数量,如果x1x_1x1为负数,表示需要从ana_nan挪给a1a_1a1
在这里插入图片描述
最小操作数是∣x1∣+∣x2∣+...+∣xn∣|x_1|+|x_2|+...+|x_n|x1+x2+...+xn
调整后,每一行感兴趣摊位点的数量相同,都为平均值a=a1+a2+...+anna=\frac{a_1+a_2+...+a_n}{n}a=na1+a2+...+an
要求满足
a1−x1+x2=aa_1-x_1+x_2=aa1x1+x2=a
a2−x2+x3=aa_2-x_2+x_3=aa2x2+x3=a

an−1−xn−1+xn=aa_{n-1}-x_{n-1}+x_n=aan1xn1+xn=a
an−xn+x1=aa_{n}-x_{n}+x_1=aanxn+x1=a
这是一个包含n个未知数、n个方程的方程组,但并不是有唯一解,因为把左边、右边全部加起来,会发现这是一个恒等式,所以上边实际上最多只有n-1个完全独立的方程。
转换上述方程组
x1−x2=a1−ax_1-x_2=a_1-ax1x2=a1a
x2−x3=a2−ax_2-x_3=a_2-ax2x3=a2a

xn−1−xn=an−1−ax_{n-1}-x_n=a_{n-1}-axn1xn=an1a
xn−x1=an−ax_{n}-x_1=a_{n}-axnx1=ana
进一步,用x1x_1x1表示所有变量,
x1=x1−0=x1−c1x_1=x_1-0=x_1-c_1x1=x10=x1c1
x2=x1−(a1−a)=x1−c2x_2=x_1-(a_1-a)=x_1-c_2x2=x1(a1a)=x1c2
x3=x2−(a2−a)=x1−(a1−a)−(a2−a)=x1−(a1+a2−2a)=x1−c3x_3=x_2-(a_2-a)=x_1-(a_1-a)-(a_2-a)=x_1-(a_1+a_2-2a)=x_1-c_3x3=x2(a2a)=x1(a1a)(a2a)=x1(a1+a22a)=x1c3

xn=x1−(a1+a2+...+an−1−(n−1)a)=xn−cnx_{n}=x_1-(a_1+a_2+...+a_{n-1}-(n-1)a)=x_n-c_nxn=x1(a1+a2+...+an1(n1)a)=xncn
那么
∣x1∣+∣x2∣+...+∣xn∣|x_1|+|x_2|+...+|x_n|x1+x2+...+xn=∣x1−c1∣+∣x1−c2∣+...+∣x1−cn∣|x_1-c_1|+|x_1-c_2|+...+|x_1-c_n|x1c1+x1c2+...+x1cn
c1、...、cnc_1、...、c_nc1...cn看作是数轴上的点,就转换成了求x1x_1x1到数轴上的点的距离之和的最小值,当为中点时,值最小。

#include <cstdio>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;const int N = 100010;int row[N], col[N], s[N], c[N];LL work(int n, int a[])
{for (int i  = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i];if (s[n] % n) return -1;  // 不能整除int avg = s[n] / n;c[1] = 0;for (int i = 2; i <= n; i ++ ) c[i] = s[i - 1] - (i - 1) * avg;sort(c + 1, c + n);LL res = 0;for (int i = 1; i <= n; i ++ ) res += abs(c[i] - c[(n + 1) / 2]);return res;
}int main()
{int n, m, cnt;scanf("%d%d%d", &n, &m,& cnt);while (cnt -- ){int x, y;scanf("%d%d", &x, &y);row[x] ++ , col[y] ++ ;}LL r = work(n, row);LL c = work(m, col);if (r != -1 && c != -1) printf("both %lld\n", r + c);else if (r != -1) printf("row %lld\n", r);else if (c != -1) printf("column %lld\n", c);else printf("impossible\n");return 0;
}

动态中位数
我们只关心中位数,不需要对较小的数排序,也不需要对较大的数排序,所以可以用2个堆来做。对顶堆
当输入是偶数个数的时候,上下2个堆的数量一样多;当输入是奇数个数的时候,下边的堆数量比上边多一个。
核心是维护这个堆
1️⃣上边所有元素都≥下面的所有元素;
2️⃣下面的个数最多比上边多1;
算法复杂度O(nlogn)O(nlog^n)O(nlogn),思想很重要。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>using namespace std;int T, k, n;int main()
{scanf("%d", &T);  // 多组测试数据while (T--){scanf("%d%d", &k, &n);  // 编号、数据集个数printf("%d %d\n", k, (n + 1) / 2);priority_queue<int> down;  // 默认大根堆priority_queue<int, vector<int>, greater<int>> up;  // 小根堆int cnt = 0;  // 记录当前已经输出了多少数for (int i = 0; i < n; i ++ ){int x;scanf("%d", &x);if (down.empty() || x <= down.top()) down.push(x);else up.push(x);if (down.size() < up.size()) {down.push(up.top());up.pop();} else if (down.size() > up.size() + 1) {up.push(down.top());down.pop();}if (i % 2 == 0){printf("%d ", down.top());if ( ++ cnt % 10 == 0) puts("");}}if (cnt % 10) puts("");}return 0;
}

超快速排序
有k个逆序对,则至少需要操作k次。
每一次交换,都是交换的前一个数>后一个数的场景(ai>ai+1,i<i+1a_i>a_{i+1},i<i+1ai>ai+1,i<i+1),只要这样操作k次,就能保证逆序对为0,所有数为有序。
最小值是k,且一定存在一个操作次数为k的方案。问题就转换成了求逆序对的数量。

#include <cstdio>using namespace std;using LL = long long;const int N = 500010;int n;
LL q[N], w[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 i = l, j = mid + 1, k = 0;while (i <= mid && j <= r){if (q[i] <= q[j]) w[k ++ ] = q[i ++ ];else{res += mid - i + 1;w[k ++ ] = q[j ++ ];}}while (i <= mid) w[k ++ ] = q[i ++ ];while (j <= r) w[k ++ ] = q[j ++ ];for (i = l, j = 0; i <= r; i ++ , j ++ ) q[i] = w[j];return res;
}int main()
{while (scanf("%d", &n), n){for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);printf("%lld\n", merge_sort(0, n - 1));}
}

RMQ

天才的记忆
可以使用线段树做,而且只有查询没有修改
RMQ(Range Minimum/Maximum Query)区间最值算法
很常用、代码很短
也叫ST表,本质上是一个动态规划。
在这里插入图片描述

f(i,j)f(i, j)f(i,j)表示从iii开始,长度是2j2^j2j的区间中,最大值是多少。
f(i,j)=max{f(i,j−1),f(i+2j−1,j−1)}f(i, j)=max\{f(i, j-1), f(i+2^{j-1},j-1)\}f(i,j)=max{f(i,j1),f(i+2j1,j1)}
状态数量,第一维是n个,第二维是lognlog^nlogn
状态转移是O(1)O(1)O(1)的,所以总的时间复杂度是O(nlogn)O(nlog^n)O(nlogn)的。
当预处理完之后,怎么求呢?
比如要求区间[l,r][l,r][l,r]之间的最大值,区间长度为lenlenlen,先找到满足2k≤len2^k \leq len2klen的最大值,那么区间的最大值就是max{f(l,k),f(r−2k+1,k)}max \{ f(l, k), f(r-2^k+1, k) \}max{f(l,k),f(r2k+1,k)},查询是O(1)O(1)O(1)的。
在这里插入图片描述
有什么缺点呢?
不支持修改,是一个静态算法,要求原来的数组不能变化。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>using namespace std;const int N = 200010, M = 18;int n, m;
int w[N];
int f[N][M];void init()
{for (int j = 0; j < M; j ++ )  // 枚举区间长度,2^jfor (int i = 1; i + (1 << j) - 1 <= n; i ++ )if (!j) f[i][j] = w[i];else f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);  // 当前区间的左右子区间的最大值
}int query(int l, int r)
{int len = r - l  + 1;int k = log2(len);return max(f[l][k], f[r - (1 << k) + 1][k]);
}int main()
{scanf("%d", &n);for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);init();scanf("%d", &m);while (m -- ){int l, r;scanf("%d%d", &l, &r);printf("%d\n", query(l, r));}return 0;
}
http://www.dtcms.com/a/442863.html

相关文章:

  • 合肥做微网站建设做网站和做程序一样吗
  • 【OpenCV】图像处理实战:边界填充与阈值详解
  • 泉州网站排名哈尔滨seo优化大家
  • Upgrade Win11 subsystem Ubuntu22.04 to ubuntu24.04
  • Ubuntu 24.04 LTS 发行说明
  • 做网站需要网站负责人网站企业模板
  • 《构建可靠 Python 项目:测试金字塔的实践指南与工具解析》
  • 做医药商城网站的公司网站开发角色分配权限
  • 旅游网站建设的建议信阳网站设计
  • 技术驱动增长:赋能您的电竞体育平台快速启航与商业成功
  • 网站页面设计价格网站做网站
  • Unity游戏基础-5(一些细节)
  • 浙江建设厅网站做门户网站难吗
  • 【MySQL|第二篇】基础篇下
  • 电子商务网站规划的原则视差设计网站
  • 打卡hot100
  • wordpress怎么弄网站做淘宝网站需要多大空间
  • 在springboot项目中使用redis实现锁
  • Spring IoC 超清晰讲解:Bean、容器、依赖注入全流程
  • 政务公开网站建设方案小说网站建设详细流程
  • Next.js create-next-app命令介绍
  • 如何做一个自己的网站公司建设网站需求分析报告
  • 《API网关在智能制造MES联动中的实战应用》
  • 番禺网站建设知乎qq中心官方网站
  • 阿里云 网站部署网站更换程序
  • JavaScript 输出
  • AngularJS Bootstrap:深入浅出指南
  • vs2015做的网站广东广州重大新闻
  • 机器学习决策树与大模型的思维树
  • 宁波建站wordpress网站安装插件