Water Balance(单调栈)

该篇文章讨论的是在给定一排装有不同水量的水箱问题中,通过一系列操作重新分配水以达到字典序最小的水量分布。关键思路是利用单调栈来决定何时合并水箱,确保每次加入的新水量都尽可能地降低整体均值。
摘要由CSDN通过智能技术生成

Codeforces Round 618 (Div. 2)

E. Water Balance


E. Water Balance

题意:

一排有 n n n 个水箱,其中 i i i 个水箱装有 a i a_i ai 升水。水箱从左到右依次编号为 1 1 1 n n n

您可以执行以下操作:选择某个子段 [ l , r ] [l, r] [l,r] ( 1 ≤ l ≤ r ≤ n 1\le l \le r \le n 1lrn )( 1 ≤ l ≤ r ≤ n 1\le l \le r \le n 1lrn ),重新均匀分配水箱 l , l + 1 , … , r l, l+1, \dots, r l,l+1,,r 中的水。换句话说,用 a l + a l + 1 + ⋯ + a r r − l + 1 \frac{a_l + a_{l+1} + \dots + a_r}{r-l+1} rl+1al+al+1++ar 替换每个 a l , a l + 1 , … , a r a_l, a_{l+1}, \dots, a_r al,al+1,,ar 。例如,如果选择 [ 1 , 3 , 6 , 7 ] [1, 3, 6, 7] [1,3,6,7] 中的 l = 2 , r = 3 l = 2, r = 3 l=2,r=3 ,新的水量将是 [ 1 , 4.5 , 4.5 , 7 ] [1, 4.5, 4.5, 7] [1,4.5,4.5,7]您可以多次此操作。

你能得到的最小水量的字典序是多少?

请注意

序列 a a a 在词法上比相同长度的序列 b b b 小,当且仅当以下条件成立时:在 a a a b b b 不同的第一个(最左边)位置,序列 a a a 中的元素比 b b b 中的相应元素小。

思路:

因为是字典序最小,所以在所有序列中第一个数最小的就是最小序列,然后才看第二个,第三个等等。因此首要任务就是如何找到这个最小的第一个数。

假设我们已经决定好了将前 i i i 个数进行均摊,现在要加入第 i + 1 i+1 i+1 个数,当然希望这个数比前面数的均值要小,这样就可以进一步拉低前面数的均值。如果第 i + 1 i+1 i+1 个数大于前面的均值的话,如果后面还有足够小的数,也是有可能拉低均值的。如果满足这个情况的话,假如说第 j j j 个数是那个小数,那么区间 [ i + 1 , j ] [i+1,j] [i+1,j] 的均值应该要小于 [ 1 , i ] [1,i] [1,i],而在此之前,区间 [ i + 1 , j − 1 ] [i+1,j-1] [i+1,j1] 的均值一定小于第 j j j 个数。

也就是说,如果下一个数比前面的整体的均值要小,那么就一定可以合并到前面的整体中去,如果导致这个整体的均值小于了前面的均值,就再把这个整体合并到前面的整体。而如果下一个数比前面的整体大,就暂时放到后面观察情况。这个思路很像单调栈。

code:

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;

int n,a[maxn];
deque<pair<ll,ll> > q;

int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		ll tot=a[i],len=1;
		while(!q.empty() && q.back().second*tot<=q.back().first*len){
			tot+=q.back().first;
			len+=q.back().second;
			q.pop_back();
		}
		q.push_back(make_pair(tot,len));
	}
	while(!q.empty()){
		for(int i=1;i<=q.front().second;i++)
			printf("%.9lf\n",1.*q.front().first/q.front().second);
		q.pop_front();
	}
	return 0;
} 
  • 15
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值