【AtCoder072F】Dam

来源:互联网 发布:网购秒杀软件 编辑:程序博客网 时间:2024/06/05 11:09

题目大意

  给你一个水坝,最多装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<set>#include<map>#include<queue>#include<cmath>#include<string>#include<cstdio>#include<vector>#include<cstring>#include<iostream>#include<algorithm>#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&&head<tail) {            if (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;}
0 0