Water Balance(CF618E/C)(单调栈)

题目

给你一个数列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,每次选择一段区间 [ l , r ] [l,r] [l,r],将其中每个数变为他们的平均数,问最小字典序?
1 ≤ n ≤ 1 0 6 , 1 ≤ a i ≤ 1 0 9 1\le n\le 10^6,1\le a_i\le10^9 1n106,1ai109

思路

TM傻逼题
发现 、 i 、i i 前的操作不会改变 i i i 前缀和 ,记前缀和为 p i p_i pi ,一次修改操作相当于将

p i = p l − 1 + p r − p l − 1 r − ( l − 1 ) ∗ ( i − l + 1 ) p_i=p_{l-1}+\frac{p_r-p_{l-1}}{r-(l-1)}*(i-l+1) pi=pl1+r(l1)prpl1(il+1)

( i , p i ) (i,p_i) (i,pi) 看成点后,这TM不就是两点连线吗
求字典序最小然后就是维护一个下凸壳,单调栈即可(不会的学学斜率优化)

代码

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstring>
#include<climits>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
inline int read() {
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c==EOF)exit(0);if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define eps 1e-10
#define MAXN 1000000
#define INF 0x3f3f3f3f
double p[MAXN+5];
int Stk[MAXN+5],tp;
int main(){
	int n=read();
	for(int i=1;i<=n;i++)
		p[i]=p[i-1]+read();
	Stk[++tp]=0;
	for(int i=1;i<=n;i++){
		while(tp>=2&&(p[i]-p[Stk[tp]])/(i-Stk[tp])<(p[i]-p[Stk[tp-1]])/(i-Stk[tp-1]))
			tp--;
		Stk[++tp]=i;
	}
	for(int i=2;i<=tp;i++){
		double tmp=(p[Stk[i]]-p[Stk[i-1]])/(Stk[i]-Stk[i-1]);
		for(int j=Stk[i-1]+1;j<=Stk[i];j++)
			printf("%.10lf\n",tmp);
	}
	return 0;
}

反省

一定要多寻找题目特殊点,考试不要慌(老毛病又犯了)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值