【AtCoder072F】Dam

题目大意

  给你一个水坝,最多装 L 升水。每天早上有 Vi 的水流入,温度为 ti ,晚上自由控制流出,但要保证第二天水不会溢出。现求使第 i 天满水时水温最大值。
  水混合时满足新的体积 V=V1+V2 ,水温 t=V1×t1+V2×t2V1+V2 .
   N5×105


Solution

  感觉题目很神奇啊,一开始没有读懂题目什么意思,后来发现看漏了一句话。
  水温可以看作是点 (V,V×t) 和原点连线的斜率。这样把每份水都看作向量 (Vi,Vi×ti) ,那么混合时只要相加就可以了。
  
  通过这样我们可以发现一些性质(也可以直接发现):
  1.当前水温小于以前水温时必然会拉低总水温,所以一定会和前面的水混合,直接向前不断合并即可。
  2.当前水温大于前面时,直接将前面舍弃可以得到更高的温度,但要求总量必须为 L ,这样有可能出现不够加满水坝的情况,因此还要保留一段。
  因此,通过一个队列就可以解决如上问题。
  
  

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define rep(i,a,b) for (int i=a; i<=b; i++)
#define per(i,a,b) for (int i=a; i>=b; i--)
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1; char ch=getchar();
    while (!(ch>='0'&&ch<='9')) {if (ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {x=x*10+(ch-'0'); ch=getchar();}
    return x*f;
}

const int N = 500005;
const double eps = 1e-8;

int n,head=0,tail=0;
double L,q[N][2];

int main() {

    #ifndef ONLINE_JUDGE
        freopen("data.in","r",stdin);
        freopen("data.out","w",stdout);
    #endif

    n=read(); L=read();
    double S=0,V=0;
    rep(i,1,n) {
        int t=read(),v=read();
        V+=v; S+=(double)t*v;
        while (V>L&&headif (V-L>=q[head][0]) {
                V-=q[head][0]; S-=q[head][0]*q[head][1];
                head++;
            } else {
                q[head][0]-=V-L; S-=(V-L)*q[head][1]; V=L;
                break;
            }
        }
        q[tail][0]=v; q[tail++][1]=t;
        printf("%lf\n",S/L);
        while (tail-head>=2) {
            if (q[tail-1][1]>q[tail-2][1]) break;
            q[tail-2][1]=(q[tail-2][0]*q[tail-2][1]+q[tail-1][0]*q[tail-1][1])/(q[tail-2][0]+q[tail-1][0]);
            q[tail-2][0]+=q[tail-1][0];
            tail--;
        }
    }

    return 0;
}

你可能感兴趣的:(AtCoder)