'commit'
This commit is contained in:
52
TangDou/AcWing_TiGao/T6/PaiXu/105.cpp
Normal file
52
TangDou/AcWing_TiGao/T6/PaiXu/105.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
typedef long long LL;
|
||||
|
||||
const int N = 100010;
|
||||
|
||||
int row[N], col[N], s[N], c[N];
|
||||
|
||||
LL solve(int n, int a[]) {
|
||||
int sum = 0;
|
||||
for (int i = 1; i <= n; i++) sum += a[i];
|
||||
// 不能整除,最终无法完成平均工作
|
||||
if (sum % n) return -1;
|
||||
// 平均数
|
||||
int avg = sum / n;
|
||||
|
||||
// 构建c数组
|
||||
for (int i = 1; i <= n; i++) c[i] = c[i - 1] + a[i] - avg;
|
||||
// 排序,为求中位数做准备
|
||||
sort(c + 1, c + n + 1);
|
||||
// 计算每个c[i]与中位数的差,注意下标从1开始时的写法 c[(n+1)/2]
|
||||
LL res = 0;
|
||||
for (int i = 1; i <= n; i++) res += abs(c[i] - c[(n + 1) / 2]);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int n, m, T; // n行,m列,对T个摊点感兴趣
|
||||
int main() {
|
||||
// 加快读入
|
||||
ios::sync_with_stdio(false), cin.tie(0);
|
||||
cin >> n >> m >> T;
|
||||
while (T--) {
|
||||
int x, y;
|
||||
cin >> x >> y;
|
||||
row[x]++, col[y]++; // x行感兴趣的摊点数+1,y列感兴趣的摊点数+1
|
||||
}
|
||||
|
||||
LL r = solve(n, row), c = solve(m, col);
|
||||
|
||||
if (~r && ~c)
|
||||
printf("both %lld\n", r + c);
|
||||
else if (~r)
|
||||
printf("row %lld\n", r);
|
||||
else if (~c)
|
||||
printf("column %lld\n", c);
|
||||
else
|
||||
printf("impossible\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
155
TangDou/AcWing_TiGao/T6/PaiXu/105.md
Normal file
155
TangDou/AcWing_TiGao/T6/PaiXu/105.md
Normal file
@@ -0,0 +1,155 @@
|
||||
##[$AcWing$ $105$ 七夕祭](https://www.acwing.com/problem/content/description/107/)
|
||||
|
||||
**前序题单**
|
||||
|
||||
**[$AcWing$ $104$. 货仓选址](https://www.cnblogs.com/littlehb/p/15459501.html)**
|
||||
|
||||
**[$AcWing$ $122$ 糖果传递](https://www.cnblogs.com/littlehb/p/16437042.html)**
|
||||
|
||||
**[第$13$届蓝桥杯青少年组$C++$第$5$题 金箍棒 ](https://www.cnblogs.com/littlehb/p/17137629.html)**
|
||||
|
||||
### 一、题目描述
|
||||
七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。
|
||||
|
||||
于是 $TYVJ$ 今年举办了一次线下七夕祭。
|
||||
|
||||
$Vani$ 同学今年成功邀请到了 $cl$ 同学陪他来共度七夕,于是他们决定去 $TYVJ$ 七夕祭游玩。
|
||||
|
||||
$TYVJ$ 七夕祭和 $11$ 区的夏祭的形式很像。
|
||||
|
||||
矩形的祭典会场由 $N$ 排 $M$ 列共计 $N×M$ 个摊点组成。
|
||||
|
||||
虽然摊点种类繁多,不过 $cl$ 只对其中的一部分摊点感兴趣,比如章鱼烧、苹果糖、棉花糖、射的屋……什么的。
|
||||
|
||||
$Vani$ 预先联系了七夕祭的负责人 $zhq$,希望能够通过恰当地布置会场, **使得各行中 $cl$ 感兴趣的摊点数一样多,并且各列中 $cl$ 感兴趣的摊点数也一样多**。
|
||||
|
||||
不过 $zhq$ 告诉 $Vani$,摊点已经随意布置完毕了,如果想满足 $cl$ 的要求,**唯一的调整方式就是交换两个相邻的摊点**。
|
||||
|
||||
两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上。
|
||||
|
||||
由于 $zhq$ 率领的 $TYVJ$ 开发小组成功地扭曲了空间,**每一行或每一列的第一个位置和最后一个位置也算作相邻**。
|
||||
|
||||
现在 $Vani$ 想知道他的两个要求最多能满足多少个。
|
||||
|
||||
在此前提下,至少需要交换多少次摊点。
|
||||
|
||||
|
||||
**输入格式**
|
||||
第一行包含三个整数 $N$ 和 $M$ 和 $T$,$T$ 表示 $cl$ 对多少个摊点感兴趣。
|
||||
|
||||
接下来 $T$ 行,每行两个整数 $x,y$,表示 $cl$ 对处在第 $x$ 行第 $y$ 列的摊点感兴趣。
|
||||
|
||||
**输出格式**
|
||||
首先输出一个字符串。
|
||||
|
||||
如果能满足 $Vani$ 的全部两个要求,输出 $both$;
|
||||
|
||||
如果通过调整只能使得各行中 $cl$ 感兴趣的摊点数一样多,输出 $row$;
|
||||
|
||||
如果只能使各列中 $cl$ 感兴趣的摊点数一样多,输出 $column$;
|
||||
|
||||
如果均不能满足,输出 $impossible$。
|
||||
|
||||
如果输出的字符串不是 $impossible$, 接下来输出 **最小交换次数**,与字符串之间用一个空格隔开。
|
||||
|
||||
**数据范围**
|
||||
$1≤N,M≤100000,0≤T≤min(N∗M,100000),1≤x≤N,1≤y≤M$
|
||||
|
||||
**输入样例**:
|
||||
```cpp {.line-numbers}
|
||||
2 3 4
|
||||
1 3
|
||||
2 1
|
||||
2 2
|
||||
2 3
|
||||
```
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
row 1
|
||||
```
|
||||
|
||||
### 二、问题分析
|
||||
解法涉及一个贪心模板 ,请先看透这个题 :**[糖果传递](https://www.cnblogs.com/littlehb/p/16437042.html)**
|
||||
|
||||
|
||||
首先提醒一下,在一行中,各列摊位之间交换位置,是不改变行的摊位数量的,列同理。
|
||||
|
||||
模拟一下交换的过程:
|
||||
|
||||
假设七夕祭有$12$个摊位,图中有红圈的是题目主角喜欢的摊位。
|
||||
|
||||
经过两轮交换后各列的摊位的红圈的数量都一样了,但各行的红圈数量没有发生过变化。
|
||||
|
||||
<center><img src='https://cdn.acwing.com/media/article/image/2022/07/02/64630_f4288607f9-006eb5E0gy1gcj8m14efqj30x709s3yx.jpg'></center>
|
||||
|
||||
|
||||
这个题和 **糖果传递** 那个题有什么关联呢?
|
||||
|
||||
别急,我先把这个图改一改(把线擦去了)。
|
||||
<center><img src='https://cdn.acwing.com/media/article/image/2022/07/02/64630_f7bdb50ef9-006eb5E0gy1gcj8qvp4kej30xd09ft95.jpg'></center>
|
||||
|
||||
你们看,这些红圈像不像糖果,哈哈哈哈哈哈哈哈,相邻列之间交换摊位,就像是相邻两个小朋友正交换糖果嘛。
|
||||
|
||||
**算法思路**:
|
||||
因为行之间的交换苹果,并不影响列;列之间交换苹果,并不影响行,现在我们想求的是
|
||||
$$\large min(行变更次数+列变更次数)$$
|
||||
|
||||
而行变更与列变更是个自独立的,我们就可以先计算行变更最小值,再计算列变更最小值,加在一起就是答案。
|
||||
|
||||
**总结:就是一个两遍糖果传递**
|
||||
|
||||
### 三、实现代码
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
typedef long long LL;
|
||||
|
||||
const int N = 100010;
|
||||
|
||||
int row[N], col[N], s[N], c[N];
|
||||
|
||||
LL solve(int n, int a[]) {
|
||||
int sum = 0;
|
||||
for (int i = 1; i <= n; i++) sum += a[i];
|
||||
// 不能整除,最终无法完成平均工作
|
||||
if (sum % n) return -1;
|
||||
// 平均数
|
||||
int avg = sum / n;
|
||||
|
||||
// 构建c数组
|
||||
for (int i = 1; i <= n; i++) c[i] = c[i - 1] + a[i] - avg;
|
||||
// 排序,为求中位数做准备
|
||||
sort(c + 1, c + n + 1);
|
||||
// 计算每个c[i]与中位数的差,注意下标从1开始时的写法 c[(n+1)/2]
|
||||
LL res = 0;
|
||||
for (int i = 1; i <= n; i++) res += abs(c[i] - c[(n + 1) / 2]);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int n, m, T; // n行,m列,对T个摊点感兴趣
|
||||
int main() {
|
||||
// 加快读入
|
||||
ios::sync_with_stdio(false), cin.tie(0);
|
||||
cin >> n >> m >> T;
|
||||
while (T--) {
|
||||
int x, y;
|
||||
cin >> x >> y;
|
||||
row[x]++, col[y]++; // x行感兴趣的摊点数+1,y列感兴趣的摊点数+1
|
||||
}
|
||||
|
||||
LL r = solve(n, row), c = solve(m, col);
|
||||
|
||||
if (~r && ~c)
|
||||
printf("both %lld\n", r + c);
|
||||
else if (~r)
|
||||
printf("row %lld\n", r);
|
||||
else if (~c)
|
||||
printf("column %lld\n", c);
|
||||
else
|
||||
printf("impossible\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
44
TangDou/AcWing_TiGao/T6/PaiXu/106.cpp
Normal file
44
TangDou/AcWing_TiGao/T6/PaiXu/106.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
int T;
|
||||
int main() {
|
||||
// 加快读入
|
||||
ios::sync_with_stdio(false), cin.tie(0);
|
||||
cin >> T;
|
||||
|
||||
while (T--) {
|
||||
int n, m;
|
||||
cin >> m >> n;
|
||||
// 对于每个数据集,第一行输出两个整数,分别代表数据集的编号以及
|
||||
// 输出中位数的个数(应为数据个数加一的二分之一),数据之间用空格隔开。
|
||||
printf("%d %d\n", m, (n + 1) / 2);
|
||||
|
||||
priority_queue<int, vector<int>, greater<int>> up; // 小顶堆
|
||||
priority_queue<int> down; // 默认大顶堆
|
||||
|
||||
// 对顶堆
|
||||
int cnt = 0;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
int x;
|
||||
cin >> x;
|
||||
|
||||
if (!up.size() || x >= up.top())
|
||||
up.push(x);
|
||||
else
|
||||
down.push(x);
|
||||
|
||||
if (up.size() > down.size() + 1) down.push(up.top()), up.pop();
|
||||
if (down.size() > up.size()) up.push(down.top()), down.pop();
|
||||
|
||||
// 奇数才输出
|
||||
if (i & 1) {
|
||||
printf("%d ", up.top());
|
||||
if (++cnt % 10 == 0) puts(""); // 题目要求十个一换行
|
||||
}
|
||||
}
|
||||
// 最后不足10个,也需要输出一个的换行
|
||||
if (cnt % 10) puts("");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
114
TangDou/AcWing_TiGao/T6/PaiXu/106.md
Normal file
114
TangDou/AcWing_TiGao/T6/PaiXu/106.md
Normal file
@@ -0,0 +1,114 @@
|
||||
##[$AcWing$ $106$. 动态中位数](https://www.acwing.com/problem/content/description/108/)
|
||||
|
||||
### 一、题目描述
|
||||
依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出 **已读入的整数** 构成的序列的 **中位数**。
|
||||
|
||||
**输入格式**
|
||||
第一行输入一个整数 $P$,代表后面数据集的个数,接下来若干行输入各个数据集。
|
||||
|
||||
每个数据集的第一行首先输入一个代表数据集的编号的整数。
|
||||
|
||||
然后输入一个整数 $M$,代表数据集中包含数据的个数,$M$ 一定为奇数,数据之间用空格隔开。
|
||||
|
||||
数据集的剩余行由数据集的数据构成,每行包含 $10$ 个数据,最后一行数据量可能少于 $10$ 个,数据之间用空格隔开。
|
||||
|
||||
**输出格式**
|
||||
对于每个数据集,第一行输出两个整数,分别代表数据集的编号以及输出中位数的个数(**应为数据个数加一的二分之一**),数据之间用空格隔开。
|
||||
|
||||
数据集的剩余行由输出的中位数构成,每行包含 $10$ 个数据,最后一行数据量可能少于 $10$ 个,数据之间用空格隔开。
|
||||
|
||||
输出中不应该存在空行。
|
||||
|
||||
**数据范围**
|
||||
$1≤P≤1000,1≤M≤99999,$
|
||||
所有 $M$ 相加之和不超过 $5×10^5$。
|
||||
|
||||
**输入样例:**
|
||||
```cpp {.line-numbers}
|
||||
3
|
||||
1 9
|
||||
1 2 3 4 5 6 7 8 9
|
||||
2 9
|
||||
9 8 7 6 5 4 3 2 1
|
||||
3 23
|
||||
23 41 13 22 -3 24 -31 -11 -8 -7
|
||||
3 5 103 211 -311 -45 -67 -73 -81 -99
|
||||
-33 24 56
|
||||
```
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
1 5
|
||||
1 2 3 4 5
|
||||
2 5
|
||||
9 8 7 6 5
|
||||
3 12
|
||||
23 23 22 22 13 3 5 5 3 -3
|
||||
-7 -3
|
||||
```
|
||||
|
||||
|
||||
### 二、算法分析
|
||||
|
||||
由于是只关心中位数,而且是一个动态不停求中位数的过程,这有一个经典的作法:**对顶堆**
|
||||
|
||||

|
||||
|
||||
- 1、 开两个堆,一个是大根堆,一个是小根堆,小根堆在上,大根堆在下
|
||||
小于中位数的都放在大根堆(下面的堆),大于中位数的都放在小根堆(上面的堆)
|
||||
此时,两个堆挨着的位置就是中位数。
|
||||
这两个堆满足了两个性质:
|
||||
- ① 上面的所有元素都大于等于下面的所有元素
|
||||
- ② 下面的个数最多比上面多$1$个
|
||||
|
||||
> **注意**:两个堆其实是没有交集的,要注意!
|
||||
|
||||
- 2、假设最初时我们的两个堆都已经维护好了,现在新来了一个数字$x$,我们把它与中位数进行比较,如果大于中位数,就放到上面去,否则就放到下面去。这样操作,我们就满足了 ① 操作。但 ②怎么来满足呢?这个也简单,就是判断一下现在操作的堆中元素数量与另一个堆的数量,是不是不再满足最多多一个,如果是的话,就把 **堆顶** 的那个元素弹出,并且加入到另一端去。
|
||||
> **思考**:为什么是堆顶的呢?因为只有堆顶的才是两者的分界线啊。
|
||||
|
||||
### 三、实现代码
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
int T;
|
||||
int main() {
|
||||
// 加快读入
|
||||
ios::sync_with_stdio(false), cin.tie(0);
|
||||
cin >> T;
|
||||
|
||||
while (T--) {
|
||||
int n, m;
|
||||
cin >> m >> n;
|
||||
// 对于每个数据集,第一行输出两个整数,分别代表数据集的编号以及
|
||||
// 输出中位数的个数(应为数据个数加一的二分之一),数据之间用空格隔开。
|
||||
printf("%d %d\n", m, (n + 1) / 2);
|
||||
|
||||
priority_queue<int, vector<int>, greater<int>> up; // 小顶堆
|
||||
priority_queue<int> down; // 默认大顶堆
|
||||
|
||||
// 对顶堆
|
||||
int cnt = 0;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
int x;
|
||||
cin >> x;
|
||||
|
||||
if (!up.size() || x >= up.top())
|
||||
up.push(x);
|
||||
else
|
||||
down.push(x);
|
||||
|
||||
if (up.size() > down.size() + 1) down.push(up.top()), up.pop();
|
||||
if (down.size() > up.size()) up.push(down.top()), down.pop();
|
||||
|
||||
// 奇数才输出
|
||||
if (i & 1) {
|
||||
printf("%d ", up.top());
|
||||
if (++cnt % 10 == 0) puts(""); // 题目要求十个一换行
|
||||
}
|
||||
}
|
||||
// 最后不足10个,也需要输出一个的换行
|
||||
if (cnt % 10) puts("");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
38
TangDou/AcWing_TiGao/T6/PaiXu/107.cpp
Normal file
38
TangDou/AcWing_TiGao/T6/PaiXu/107.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
typedef long long LL;
|
||||
const int N = 500010;
|
||||
int n;
|
||||
LL q[N], t[N];
|
||||
LL ans; // 结果
|
||||
|
||||
void merge_sort(LL 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 i = l, j = mid + 1, k = 0;
|
||||
while (i <= mid && j <= r)
|
||||
if (q[i] <= q[j])
|
||||
t[k++] = q[i++];
|
||||
else {
|
||||
ans += mid - i + 1; // 如果q[i]>q[j],那么 i~mid之间所有数字,都>q[j]
|
||||
t[k++] = q[j++];
|
||||
}
|
||||
// 清理剩余
|
||||
while (i <= mid) t[k++] = q[i++];
|
||||
while (j <= r) t[k++] = q[j++];
|
||||
// 抄回来
|
||||
for (i = l, j = 0; i <= r; i++, j++) q[i] = t[j];
|
||||
}
|
||||
|
||||
int main() {
|
||||
while (cin >> n, n) {
|
||||
for (int i = 1; i <= n; i++) cin >> q[i];
|
||||
ans = 0;
|
||||
merge_sort(q, 1, n);
|
||||
printf("%lld\n", ans);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
129
TangDou/AcWing_TiGao/T6/PaiXu/107.md
Normal file
129
TangDou/AcWing_TiGao/T6/PaiXu/107.md
Normal file
@@ -0,0 +1,129 @@
|
||||
##[$AcWing$ $107$. 超快速排序 ](https://www.acwing.com/problem/content/description/109/)
|
||||
|
||||
### 一、题目描述
|
||||
在这个问题中,您必须分析特定的排序算法---- **超快速排序**。
|
||||
|
||||
该算法通过交换两个相邻的序列元素来处理 $n$ 个不同整数的序列,直到 **序列按升序排序**。
|
||||
|
||||
对于输入序列 `9 1 0 5 4`,超快速排序生成输出 `0 1 4 5 9`。
|
||||
|
||||
您的任务是确定超快速排序需要执行多少交换操作才能对给定的输入序列进行排序。
|
||||
|
||||
**输入格式**
|
||||
输入包括一些测试用例。
|
||||
|
||||
每个测试用例的第一行输入整数 $n$,代表该用例中输入序列的长度。
|
||||
|
||||
接下来 $n$ 行每行输入一个整数 $a_i$,代表用例中输入序列的具体数据,第 $i$行的数据代表序列中第 $i$ 个数。
|
||||
|
||||
当输入用例中包含的输入序列长度为 $0$ 时,输入终止,该序列无需处理。
|
||||
|
||||
**输出格式**
|
||||
对于每个需要处理的输入序列,输出一个整数 $op$,代表对给定输入序列进行排序所需的最小交换操作数,每个整数占一行。
|
||||
|
||||
|
||||
**数据范围**
|
||||
$0≤n<500000,$一个测试点中,所有 $n$ 的和不超过 $500000$。
|
||||
$0≤a_i≤999999999$
|
||||
|
||||
**输入样例:**
|
||||
```cpp {.line-numbers}
|
||||
5
|
||||
9
|
||||
1
|
||||
0
|
||||
5
|
||||
4
|
||||
3
|
||||
1
|
||||
2
|
||||
3
|
||||
0
|
||||
```
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
6
|
||||
0
|
||||
```
|
||||
|
||||
### 二、解题思路
|
||||
#### 引理:逆序数
|
||||
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个 **逆序**。一个排列中 **逆序的总数** 就称为这个排列的**逆序数**。
|
||||
|
||||
<font color='red' size=4><b>排列的逆序数等于该排列转化为自然排序(从小到大)的最小次数</b></font>
|
||||
|
||||
例如:$31425$ 的逆序数为$3$
|
||||
|
||||
* $3$无逆序,则$n_1 = 0$,
|
||||
|
||||
* $1$逆序有$3$,则$n_2 = 1$,
|
||||
|
||||
* $4$无逆序,则$n_3 = 0$,
|
||||
|
||||
* $2$逆序有$3,4$,则$n_4 = 2$,
|
||||
|
||||
* $5$无逆序,则$n_5 = 0$,
|
||||
|
||||
若将该序列转化成从小到大排序,需要$\large (n_1 + n_2 + n_3 + n_4 + n_5)=3$次交换。
|
||||
|
||||
> **黄海理解**:因为是一个个来观察的,所以,在来到当前数字面前时,前面的可以理解为已经调整好了顺序,支付了$n_i$的代价,这样就非常好理解了。
|
||||
|
||||
#### 直接计算
|
||||
计算一个排列的逆序数的直接方法是逐个枚举逆序,同时统计个数。例如在序列 $\{3, 1, 4, 2, 5 \}$ 中,逆序依次为 $(3,1),(3,2),(4,3)$,因此该序列的逆序数为 $3$。
|
||||
|
||||
#### 归并排序
|
||||
直接计数法虽然简单直观,但是其时间复杂度是$O(n^2)$。一个更快的计算方法是在 **归并排序** 的同时计算逆序数。且该方法只是交换相邻的两个元素,符合题意。
|
||||
|
||||
### 三、算法分析
|
||||
#### 归并排序
|
||||
|
||||
* 给定两个有序序列,从右边序列中最小的元素开始往左边序列中插入,设右边序列中选定的元素的位置为j,左边序列选定的元素位置为$i$,中间位置为$mid$,则此时交换的位置次数是$(mid - i + 1)$,最后把所有归并后交换的次数累加在一起即可。
|
||||
|
||||
* 例如 $31425$ 经过归并后得出两个有序序列 $134$ 和 $25$ (注意:$314$也是需要归并交换位置得到$123$的)即将$2$插入到$3$位置前,此操作相当于交换了两次位置,设$3$元素的位置为$i$,$4$元素的位置是中间位置$mid$,则此时交换的次数为$(mid - i + 1) = 2$次
|
||||
|
||||
#### 附上主播的图
|
||||
<center><img src='https://cdn.acwing.com/media/article/image/2019/10/25/7416_8e3b27e4f6-ec8d32d62b62e7be74d4fe56db175a7.png'></center>
|
||||
|
||||
|
||||
### 四、实现代码
|
||||
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
typedef long long LL;
|
||||
const int N = 500010;
|
||||
int n;
|
||||
LL q[N], t[N];
|
||||
LL ans; // 结果
|
||||
|
||||
void merge_sort(LL 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 i = l, j = mid + 1, k = 0;
|
||||
while (i <= mid && j <= r)
|
||||
if (q[i] <= q[j])
|
||||
t[k++] = q[i++];
|
||||
else {
|
||||
ans += mid - i + 1; // 如果q[i]>q[j],那么 i~mid之间所有数字,都>q[j]
|
||||
t[k++] = q[j++];
|
||||
}
|
||||
// 清理剩余
|
||||
while (i <= mid) t[k++] = q[i++];
|
||||
while (j <= r) t[k++] = q[j++];
|
||||
// 抄回来
|
||||
for (i = l, j = 0; i <= r; i++, j++) q[i] = t[j];
|
||||
}
|
||||
|
||||
int main() {
|
||||
while (cin >> n, n) {
|
||||
for (int i = 1; i <= n; i++) cin >> q[i];
|
||||
ans = 0;
|
||||
merge_sort(q, 1, n);
|
||||
printf("%lld\n", ans);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
30
TangDou/AcWing_TiGao/T6/PaiXu/122.cpp
Normal file
30
TangDou/AcWing_TiGao/T6/PaiXu/122.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
const int N = 1000010;
|
||||
typedef long long LL;
|
||||
|
||||
LL a[N], c[N], sum, avg, n, res;
|
||||
|
||||
int main() {
|
||||
// 加快读入
|
||||
ios::sync_with_stdio(false), cin.tie(0);
|
||||
cin >> n;
|
||||
for (int i = 1; i <= n; i++) cin >> a[i], sum += a[i];
|
||||
avg = sum / n;
|
||||
|
||||
// c[k]=(a[1]+a[2]+...+a[k])-k*avg
|
||||
// c[k-1]=(a[1]+a[2]+...+a[k-1])-(k-1)*avg
|
||||
// 努力找出c[k]与 c[k-1]之间的递推关系:
|
||||
// c[k]=c[k-1]+a[k]-avg
|
||||
// 所以,c数组可以通过递推得到
|
||||
for (int k = 1; k <= n; k++) c[k] = c[k - 1] + a[k] - avg;
|
||||
|
||||
// 通过排序 => c[(n+1)/2] = 中位值
|
||||
sort(c + 1, c + n + 1);
|
||||
// 将x_n'放到中位值处,这样,几何含义上所有n 个位置上c1,c2,...cn到中位值的距离绝对值和最小
|
||||
for (int i = 1; i <= n; i++) res += abs(c[i] - c[(n + 1) / 2]);
|
||||
|
||||
cout << res << endl;
|
||||
return 0;
|
||||
}
|
||||
133
TangDou/AcWing_TiGao/T6/PaiXu/122.md
Normal file
133
TangDou/AcWing_TiGao/T6/PaiXu/122.md
Normal file
@@ -0,0 +1,133 @@
|
||||
##[$AcWing$ $122$ 糖果传递](https://www.acwing.com/problem/content/description/124/)
|
||||
|
||||
### 一、题目描述
|
||||
有 $n$ 个小朋友坐成一圈,每人有 $a[i]$ 个糖果。
|
||||
|
||||
**每人只能给左右两人传递糖果**。
|
||||
|
||||
每人每次传递一个糖果代价为 $1$。
|
||||
|
||||
**求使所有人获得均等糖果的最小代价**。
|
||||
|
||||
**输入格式**
|
||||
第一行输入一个正整数 $n$,表示小朋友的个数。
|
||||
|
||||
接下来 $n$ 行,每行一个整数 $a[i]$,表示第 $i$ 个小朋友初始得到的糖果的颗数。
|
||||
|
||||
**输出格式**
|
||||
输出一个整数,表示最小代价。
|
||||
|
||||
**数据范围**
|
||||
$1≤n≤1000000,0≤a[i]≤2×10^9,$数据保证一定有解。
|
||||
|
||||
**输入样例:**
|
||||
```cpp {.line-numbers}
|
||||
4
|
||||
1
|
||||
2
|
||||
5
|
||||
4
|
||||
```
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
4
|
||||
```
|
||||
|
||||
### 二、解题思路
|
||||
<center><img src='https://cdn.acwing.com/media/article/image/2022/07/02/64630_83fc3636f9-2.png'></center>
|
||||
|
||||
* 假设第$1$个小朋友有$a_1$颗糖果,给第$2$个小朋友$x_1$颗糖果,从$n$获得$x_n$颗糖果,此时,他有$a_1-x_1+x_n$颗糖果,同理,第$2$个有$a_2-x_2+x_1$,第$3$有...
|
||||
|
||||
* 每个小朋友的目标为平均数$avg$,列出约束方程为
|
||||
$$\large \left\{\begin{matrix}
|
||||
a_1-x_1+x_n=avg & \\
|
||||
a_2-x_2+x_1=avg & \\
|
||||
a_3-x_3+x_2=avg & \\
|
||||
... \\
|
||||
a_n-x_n+x_{n-1}=avg
|
||||
\end{matrix}\right.
|
||||
$$
|
||||
我们的目标:
|
||||
<font color='red' size=4><b>
|
||||
$$\large min(|x_1|+|x_2|+...+|x_n|)$$
|
||||
</b></font>
|
||||
|
||||
下面,我们用$x_n$来表示上面的方程组:替代$x_1,x_2,...,x_{n-1}$
|
||||
|
||||
$$\large \left\{\begin{array}{l}
|
||||
x_1=a_1+x_n-avg \\
|
||||
x_2=a_2+x_1-avg =(a_1+a_2)-2*avg+x_n & \\
|
||||
x_3=a_3+x_2-avg =(a_1+a_2+a_3)-3*avg+x_n & \\
|
||||
... \\
|
||||
x_{n-1}=(a_1+a_2+...+a_{n-1})-(n-1)*avg+x_n & \\
|
||||
\end{array}\right.
|
||||
$$
|
||||
|
||||
将$x_k$定为变量 , <font color='blue' size=4><b>常数</b></font> 定义为$c_k$,则:
|
||||
$$\large \displaystyle c_k=\sum_{i=1}^{k}a_i -k*avg$$
|
||||
|
||||
有:
|
||||
$$\large \left\{\begin{array}{l}
|
||||
x_1=c_1+x_n \\
|
||||
x_2=c_2+x_n \\
|
||||
... \\
|
||||
x_{n-1}=c_{n-1}+x_n
|
||||
\end{array}\right.
|
||||
$$
|
||||
|
||||
因为$x_1,x_n$都是可正可负的,正的表示把这些糖果给了别人,负的表示别人把这些糖果给了自己。
|
||||
|
||||
所以,可以令$x_n'=-x_n$
|
||||
|
||||
上面的方程组转化为
|
||||
$$\large \left\{\begin{array}{l}
|
||||
x_1=c_1-x_n' \\
|
||||
x_2=c_2-x_n' \\
|
||||
... \\
|
||||
x_{n-1}=c_{n-1}-x_n'
|
||||
\end{array}\right.
|
||||
$$
|
||||
|
||||
此时,我们的目标也就转化为:
|
||||
|
||||
$$\large min(|c_1-x_n'|+|c_2-x_n'|+...+|c_{n-1}-x_n'|)$$
|
||||
|
||||
**注意到** $|c_i-x_n'|$的几何意义是数轴上的点$c_i$到$x_n'$的距离,所以问题变成了:
|
||||
> **给定数轴上的$n$个点,找出一个到他们的距离之和尽量小的点,而这个点就是这些数中的中位数,问题再次转化为经典问题:
|
||||
**[$AcWing$ $104$.仓库选址](https://www.acwing.com/problem/content/106/)** ,只需要求中位数和其他数的差值的总和就可以了。**
|
||||
|
||||
### 三、实现代码
|
||||
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
const int N = 1000010;
|
||||
typedef long long LL;
|
||||
|
||||
LL a[N], c[N], sum, avg, n, res;
|
||||
|
||||
int main() {
|
||||
// 加快读入
|
||||
ios::sync_with_stdio(false), cin.tie(0);
|
||||
cin >> n;
|
||||
for (int i = 1; i <= n; i++) cin >> a[i], sum += a[i];
|
||||
avg = sum / n;
|
||||
|
||||
// c[k]=(a[1]+a[2]+...+a[k])-k*avg
|
||||
// c[k-1]=(a[1]+a[2]+...+a[k-1])-(k-1)*avg
|
||||
// 努力找出c[k]与 c[k-1]之间的递推关系:
|
||||
// c[k]=c[k-1]+a[k]-avg
|
||||
// 所以,c数组可以通过递推得到
|
||||
for (int k = 1; k <= n; k++) c[k] = c[k - 1] + a[k] - avg;
|
||||
|
||||
// 通过排序 => c[(n+1)/2] = 中位值
|
||||
sort(c + 1, c + n + 1);
|
||||
// 将x_n'放到中位值处,这样,几何含义上所有n 个位置上c1,c2,...cn到中位值的距离绝对值和最小
|
||||
for (int i = 1; i <= n; i++) res += abs(c[i] - c[(n + 1) / 2]);
|
||||
|
||||
cout << res << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
Reference in New Issue
Block a user