Link
Solution
要求字典序最小,那么前面的值要尽量小,那就不妨先考虑前面的值。两段有交部分的操作是很难处理的,肯定需要某种转换。所以想到观察两段有交的操作会产生什么样的效果。简单的分类讨论之后,会发现无论怎么操作都对前面的数没有正的贡献,反而有可能把前面的一段数变大。由于有交的操作一定不会更优,所以最后的操作一定是不相交的。那么问题就转换成了将一个序列分成若干段,满足越前面的段平均值越小越好,求最优的序列。
用增量法,假设前面已经有一个段了,考虑要不要把当前新的数加入这个段。如果合并后平均值变小了,那么合并进去更优;反之,则新开一段。但如果合并了后平均值变小了,就可能出现比更前面一段的平均值还小的情况,显然再把这两段合并更优。如此迭代。容易想到用单调栈来维护这个过程。
#include<stdio.h>
#define N 1000007
#define db double
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
struct Node{
db sum; int num;
db calc(){return sum/num;}
Node operator +(const Node &X) {
Node A;
A.sum=sum+X.sum,A.num=num+X.num;
return A;
}
}sta[N];
int n,top=0;
int main(){
n=read();
for(int i=1;i<=n;i++){
Node now; now.sum=read(),now.num=1;
while(top&&sta[top].calc()>now.calc())
now=now+sta[top--];
sta[++top]=now;
}
for(int i=1;i<=top;i++)
for(int j=1;j<=sta[i].num;j++) printf("%.9lf
",sta[i].sum/sta[i].num);
}