【题目来源】
https://www.acwing.com/problem/content/2043/
【题目描述】
贝茜对她最近在农场周围造成的一切恶作剧感到抱歉,她同意帮助农夫约翰把一批新到的干草捆堆起来。
开始时,共有 N 个空干草堆,编号 1∼N。
约翰给贝茜下达了 K 个指令,每条指令的格式为 A B,这意味着贝茜要在 A..B 范围内的每个干草堆的顶部添加一个新的干草捆。
例如,如果贝茜收到指令 10 13,则她应在干草堆 10,11,12,13 中各添加一个干草捆。
在贝茜完成了所有指令后,约翰想知道 N 个干草堆的中值高度——也就是说,如果干草堆按照高度从小到大排列,位于中间的干草堆的高度。
方便起见,N 一定是奇数,所以中间堆是唯一的。
请帮助贝茜确定约翰问题的答案。
【输入格式】
第一行包含 N 和 K。
接下来 K 行,每行包含两个整数 A,B,用来描述一个指令。
【输出格式】
输出完成所有指令后,N 个干草堆的中值高度。
【数据范围】
1≤N≤10^6,
1≤K≤25000,
1≤A≤B≤N
【输入样例】
7 4
5 5
2 4
4 6
3 5
【输出样例】
1
【样例解释】
贝茜完成所有指令后,各堆高度为 0,1,2,3,3,1,0。
将各高度从小到大排序后,得到 0,0,1,1,2,3,3,位于中间的是 1。
【算法分析】
● 差分:https://blog.csdn.net/hnjzsyjyj/article/details/132247279
本题多次对区间进行加减操作,故需用到差分。因为利用差分处理此类“多次对区间进行加减操作”的问题,可以大大降低算法的时间复杂度。这是因为,构造差分数组后,对原数组区间 [le, ri] 的加减操作就转化为对差分数组的区间端点的操作:d[le]+=x,d[ri+1]-=x。这明显大大降低了计算量,所以算法效率会很高。注意:此处的原数组及差分数组的下标都从1开始。
--------------------------------------------------- ☆☆☆☆☆ ---------------------------------------------------
示例:
对原数组 a[]=[3 5 9 10 16 18 27] (下标常从 1 开始)的区间 [2,5] 中的每个数都加 3,则等价于对差分数组执行 d[2]+=3,d[5+1]-=3。
验证:
(1)首先构建差分数组:设原数组为含有 n 个数的 a 数组(下标常从 1 开始),差分数组为 d 数组(下标常从 1 开始)。其中,令 d[1]=a[1],且对于 i∈[2,n],令 d[i]=a[i]-a[i-1]。
显然,原数组 a[]=[3 5 9 10 16 18 27] (下标常从 1 开始)的差分数组 d[] 构建过程如下:
d[1]=a[1]=3,
d[2]=a[2]-a[1]=5-3=2,
d[3]=a[3]-a[2]=9-5=4,
d[4]=a[4]-a[3]=10-9=1,
d[5]=a[5]-a[4]=16-10=6,
d[6]=a[6]-a[5]=18-16=2,
d[7]=a[7]-a[6]=27-18=9,
即,原数组 a[]=[3 5 9 10 16 18 27] (下标常从 1 开始)的差分数组为 d[]=[3 2 4 1 6 2 9]。
(2)对原数组的区间 [le, ri] 进行加减操作
对原数组 a[]=[3 5 9 10 16 18 27] (下标常从 1 开始)的区间 [2,5] 中的每个数都加 3,得 a[]=[3 8 12 13 19 18 27];而执行 d[2]+=3,d[5+1]-=3 后,得 d[2]=2+3=5,d[6]=2-3=-1,差分数组 d[] 更新为 d=[3 5 4 1 6 -1 9](下标从1开始)。故有:
a[1]=3,
a[2]=d[2]+a[1]=5+3=8,
a[3]=d[3]+a[2]=4+8=12,
a[4]=d[4]+a[3]=1+12=13,
a[5]=d[5]+a[4]=6+13=19,
a[6]=d[6]+a[5]=-1+19=18,
a[7]=d[7]+a[6]=9+18=27,
显然,结论得以验证成立。
-------------------------------------------------------------------------------------------------------------
注意:在代码层面上实践差分算法时,有一个很实用的技巧:若已知差分数组 d[i],则语句 d[i]+=d[i-1] 得到原始数组。即此时更新后的 d[i] 事实上就是之前谈及的原始数组的值。其中,i∈[1,n]。
● 注意本题中,“干草堆”与“干草捆”的不同陈述。
● 桶排序:https://blog.csdn.net/hnjzsyjyj/article/details/119819927
桶排序的思想:若待排序的值在一个有限范围内(整型)时,可设计有限个有序桶,桶号就是待排序的值。之后,将待排序的值依次装入对应的有序桶中(重复值都装入对应的桶),然后顺序输出各桶的值,便得到有序的序列。
#include<bits/stdc++.h>
using namespace std;
const int maxn=500;
int t[maxn];
int main() {
int n;
cin>>n;
int x; //x is less than maxn
for(int i=1; i<=n; i++) {
cin>>x;
t[x]++;
}
for(int j=1; j<=maxn; j++) {
while(t[j]>0) {
cout<<j<<" ";
t[j]--;
}
}
return 0;
}
/*
in:
10
20 40 32 67 40 20 89 300 400 15
out:
15 20 20 32 40 40 67 89 300 400
*/
【算法代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int d[maxn];
int main() {
int n,k;
cin>>n>>k;
for(int i=1; i<=k; i++) { //i from 1
int le,ri;
cin>>le>>ri;
d[le]++; //key operation
d[ri+1]--;
}
for(int i=1; i<=n; i++) d[i]+=d[i-1];
sort(d+1,d+n+1);
cout<<d[(n+1)>>1]<<endl;
return 0;
}
/*
in:
7 4
5 5
2 4
4 6
3 5
out:
1
*/
【参考文献】
https://www.acwing.com/solution/content/81274/
https://blog.csdn.net/hnjzsyjyj/article/details/119819927