[arc072F]Dam-[单调队列]

Description

传送门

Solution

首先我们肯定不能那么耿直地直接把水混合起来吧。。不然分分钟完球。

那么怎么找到最优解呢?假如我们把水的体积和温度按顺序插入队列,这时我们插入第i天的水。假如这个时候水量超过了L,我们要把前面的部分水排掉。

我们目前有两种排水方法:

1.不断去掉队列的队首(即最开始插入队列的水)

2.选一个位置j,把队列中位置[1,j]的水量按照比例分别减少一部分(就是在第j个位置对应的当天倒掉一部分水)

普通的队列,无法比较方法1,2的优劣。我们考虑单调队列(队列中的水的温度保持单调递增)

由于我们的队列是单调的,显然第1种更优。

那么,插入了第i天的水后,还要确保队列的单调。设当前队列的队尾下标为r。

假如说第i天的水温比r的水温低,那么假如要放水,把i和r混合后放显然会比先放掉r的水再放i的水更优。所以我们把i和r混合起来。

如此直到队列恢复单调。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define _v first
#define _t second
using namespace std;
typedef long long ll;
pair<ll,double> q[500010];
int n,L,t,v;
int l=1,r=0;
ll pour,all_water=0;double degree=0;
int main()
{
    scanf("%d%d",&n,&L);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d",&t,&v);
        while (all_water+v>L)
        {
            pour=min(q[l]._v,all_water+v-L);
            q[l]._v-=pour;
            all_water-=pour;    
            degree-=q[l]._t*pour;
            if (!q[l]._v) l++;
        }
        all_water+=v;degree+=1ll*t*v;
        q[++r]=make_pair(v,t);
        while (l<r&&q[r]._t<q[r-1]._t)
        {
            q[r-1]._t=(q[r]._t*q[r]._v+q[r-1]._t*q[r-1]._v)/(q[r]._v+q[r-1]._v);
            q[r-1]._v+=q[r]._v;r--;
        }
        printf("%.7f\n",degree/L);
    }
}

 

猜你喜欢

转载自www.cnblogs.com/coco-night/p/9690787.html