来自bzoj.org/p/Z2512(传统题)
题面
夏日那让人喘不过气的酷热将奶牛们的烦躁情绪推到了最高点。
最终,FJ决定建一个人工湖供奶牛消暑之用。
为了使湖看起来更加真实,FJ决定将湖的横截面建成N(1 <= N <= 100,000)个连续的平台高低错落的组合状,
所有的平台从左到右按1..N依次编号。
当然咯,在湖中注入水后,这些平台都将被淹没。
平台i在设计图上用它的宽度W_i(1 <= W_i <= 1,000)和高度
(你可以理解为该平台顶离FJ挖的地基的高度)H_i(1 <= H_i <= 1,000,000)来描述的。
所有平台的高度都是独一无二的。湖的边缘可以视为无限高的平台。
按FJ的设想,在坑挖好后,他会以1单位/分钟的速度往最低的那个平台上注水。
水在离开水管后立即下落,直到撞到平台顶或是更早些时候注入的水。然后,与所有常温下的水一样,它会迅速地流动、扩散。
简单起见,你可以认为这些都是在瞬间完成的。
FJ想知道,对于每一个平台,它的顶部是从哪个时刻开始,与水面的距离至少为1单位长度。
注意:数据不保证答案全部在32位整型变量的范围内。
例:
in
3
4 2
2 7
6 4ans
4
50
26
这道题看上去非常恐怖,但我们看懂题面后,也不难理解。
把图画出来,发现可以用单调栈求解:
从这张图不难看出,我们会在红色处灌水,要淹没某处,需在上面灌注高1个单位的水,很显然,这与宽度有关,淹没红色处要4个单位的水,需要4分钟。
那么,接下来要继续灌水,当水的高度达到5时,达到了蓝色区域的高度。接下来灌的水会流向低处的黄色区域。
在38秒时,水覆盖到蓝色区域。这时,要覆盖蓝色区域,宽度并不是2,而是12,因此,灌完蓝色区域需要50分钟。
难点在于:灌水点是不变的,还要遵循自然:要填高处,先填低处,因此模拟起来极其困难(普及组的题让我写出了NOI++的感觉QAQ!)
这个样例强度太低,看看这个更加复杂的样例:
in:
5
1 2
1 3
1 5
1 7
1 1
ans:
7
9
14
22
1
这个样例非常好,过了这个,整道题都没有问题了。
还是先画图
水从橙色区域灌入,将编号5和宽度1进栈(结构体)。
然后,准备两个变量l和r,赋值为5。
从high[r + 1]和high[l - 1]中取最小值,对r或l进行加减操作,并把对应的编号记录在变量id中(由于把边缘赋值为无穷大,无须担心越界),开始运行函数:
void water(int id)
{
int sum = 0;
while(!s.empty() && high[s.top().index] < high[id])
{
sum += s.top().width;
ans[s.top().index] = nt + sum;
int x = high[s.top().index];
s.pop();
if(!s.empty()) nt += sum * (min(high[s.top().index],high[id]) - x);
else nt += sum * (high[id] - x);
}
s.push({id,sum + width[id]});
return;
}
这个函数是核心代码,通过维护一个严格上升的单调栈,从而来完成灌水的任务。
如果栈不为空 && 栈顶元素的index所代表的high小于id所代表的high
{
把sum(宽度累加器)加上s.top().width,并把nt(现在的时间)与sum的和作为ans[s.top().index]。
开一个临时变量x = high[s.top().index)],然后弹出栈顶元素。
取栈顶元素的高于与id的高中的最小值,减去x,再乘以sum,加给nt。
一定要判定栈不为空,若栈为空,则只加上high[id] - x。
}
最后,把id和width[id]加入栈。
这个函数需要运行n次,每次参数都用high[l - 1] < high[r + 1] ? --l : ++r。
最后运行一次water(0),high[0]为无穷大,会把所有区域全部灌满,并将ans输出。
代码如下:
#include<iostream>
#include<stack>
using namespace std;
struct hu
{
long long index,width;
};
stack <hu> s;
long long n,ans[100005],high[100005],width[100005],minn = 1145141145,minx,nt;
void water(int id)
{
int sum = 0;
while(!s.empty() && high[s.top().index] < high[id])
{
sum += s.top().width;
ans[s.top().index] = nt + sum;
int x = high[s.top().index];
s.pop();
if(!s.empty()) nt += sum * (min(high[s.top().index],high[id]) - x);
else nt += sum * (high[id] - x);
}
s.push({id,sum + width[id]});
return;
}
int main()
{
cin >> n;
for(int i = 1;i <= n;i++)
{
cin >> width[i] >> high[i];
if(minn > high[i]) minn = high[i],minx = i;
}
high[0] = high[n + 1] = 1000000000;
int l = minx,r = minx;
s.push({minx,width[minx]});
for(int i = 1;i <= n;i++) water(high[l - 1] < high[r + 1] ? --l : ++r);
water(0);
for(int i = 1;i <= n;i++) cout << ans[i] << endl;
}
案例运行结果: