题目大意
给你一个水坝,最多装
L
升水。每天早上有
水混合时满足新的体积
N≤5×105
Solution
感觉题目很神奇啊,一开始没有读懂题目什么意思,后来发现看漏了一句话。
水温可以看作是点
(V,V×t)
和原点连线的斜率。这样把每份水都看作向量
(Vi,Vi×ti)
,那么混合时只要相加就可以了。
通过这样我们可以发现一些性质(也可以直接发现):
1.当前水温小于以前水温时必然会拉低总水温,所以一定会和前面的水混合,直接向前不断合并即可。
2.当前水温大于前面时,直接将前面舍弃可以得到更高的温度,但要求总量必须为
L
<script type="math/tex" id="MathJax-Element-68">L</script>,这样有可能出现不够加满水坝的情况,因此还要保留一段。
因此,通过一个队列就可以解决如上问题。
#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;
}