在 LIT 综教楼后有一个深坑,关于这个坑的来历,有很多种不同的说法。其中一种说法是,在很多年以前,这个坑就已经在那里了。这种说法也被大多数人认可,这是因为该坑有一种特别的结构,想要人工建造是有相当困难的。
从横截面图来看,坑底成阶梯状,由从左至右的 1..N 个的平面构成(其中 1 ≤ N ≤ 100,000),如图:
* * : * * : * * 8 * ** * 7 * ** * 6 * ** * 5 * ********* 4 <- 高度 * ********* 3 ************** 2 ************** 1 平面 | 1 |2| 3 |
每个平面 i 可以用两个数字来描述,即它的宽度 Wi 和高度 Hi,其中 1 ≤ Wi ≤ 1,000、1 ≤ Hi ≤ 1,000,000,而这个坑最特别的地方在于坑底每个平面的高度都是不同的。每到夏天,雨水会把坑填满,而在其它的季节,则需要通过人工灌水的方式把坑填满。灌水点设在坑底位置最低的那个平面,每分钟灌水量为一个单位(即高度和宽度均为 1)。随着水位的增长,水自然会向其它平面扩散,当水将某平面覆盖且水高达到一个单位时,就认为该平面被水覆盖了。
请你计算每个平面被水覆盖的时间。
灌水 水满后自动扩散 | | * | * * | * * * * V * * V * * * * * * .... * *~~~~~~~~~~~~* * ** * *~~~~** : * *~~~~**~~~~~~* * ** * *~~~~** : * *~~~~**~~~~~~* * ** * *~~~~**~~~~~~* *~~~~**~~~~~~* * ********* *~~~~********* *~~~~********* *~~~~********* *~~~~********* *~~~~********* ************** ************** ************** ************** ************** ************** 4 分钟后 26 分钟后 50 分钟后 平面 1 被水覆盖 平面 3 被水覆盖 平面 2 被水覆盖输入
输入的第一行是一个整数 N,表示平面的数量。从第二行开始的 N 行上分别有两个整数,分别表示平面的宽度和高度。
输出每个平面被水覆盖的时间。
#include
using namespace std;
struct Pit {
long long width;
int height;
int id;
Pit* prior;
Pit* next;
Pit() : width(0), height(0), id(0), prior(nullptr), next(nullptr) {}
~Pit() {}
};
long long ans[100010]; // 第id个平面需要时间ans[id]
void pourAndConnect(Pit* lowest) {
long long time = 0; // 记录当前已经使用了的时间
// 当前平面的前一个平面和后一个平面还有宽度,表示还需要倾倒水
while (lowest->prior->width != 0 || lowest->next->width != 0) {
ans[lowest->id] = time + lowest->width; // 记录当前平面的倾倒时间
// 找到前一个和后一个平面中较低的那个
Pit* low = lowest->prior->height < lowest->next->height ? lowest->prior : lowest->next;
time += (low->height - lowest->height) * lowest->width; // 计算倾倒水过程需要的时间
low->width += lowest->width; // 将当前平面和较低的平面合并
lowest->prior->next = lowest->next; // 从链表中删除当前平面
lowest->next->prior = lowest->prior;
delete lowest;
lowest = low; // 新的最低平面为较低的平面
// 判断新的一轮倾倒水的平面
if (lowest->height < lowest->prior->height && lowest->height < lowest->next->height) {
continue;
}
else if (lowest->height > lowest->prior->height) {
while (lowest->height > lowest->prior->height) {
lowest = lowest->prior;
}
}
else {
while (lowest->height > lowest->next->height) {
lowest = lowest->next;
}
}
}
ans[lowest->id] = time + lowest->width * 1; // 记录最后一个平面的倾倒时间
}
int main() {
int N = 0;
cin >> N;
Pit* head = new Pit(); // 创建一个头结点
Pit leftWall, rightWall;
leftWall.width = leftWall.id = rightWall.width = rightWall.id = 0;
leftWall.height = rightWall.height = 1000010;
leftWall.prior = rightWall.next = NULL;
Pit* current = head, * previous = &leftWall, * lowest = head;
// 输入每个平面的宽度和高度
for (int i = 0; i < N; i++) {
cin >> current->width >> current->height;
current->id = i + 1;
if (current->height < lowest->height) {
lowest = current; // 记录最低的平面
}
current->prior = previous; // 设置前一个平面
if (i == N - 1) {
current->next = &rightWall; // 最后一个平面的后一个平面为右墙
}
else {
current->next = new Pit(); // 创建一个新的平面
}
previous = current;
current = current->next;
}
pourAndConnect(lowest); // 进行倾倒和连接操作
// 输出每个平面的倾倒时间
for (int i = 0; i < N; i++) {
cout << ans[i + 1] << endl;
}
return 0;
}