CF1299C Water Balance

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\n",sta[i].sum/sta[i].num);
}
posted @ 2020-12-18 16:50  Kreap  阅读(63)  评论(0编辑  收藏  举报