AT2402 题解

题意简述

  • 给你 n 杯水,第 i 杯的水温为 ti,容量为 vi,依次倒入容量为 V 的大盆。注意每次倒入水后盆内水的总体积必须恒定为 V,且每杯水必须全部倒入,因此为防止倒进水时溢出,在倒水之前可以从盆里往外倒出一些水。求每次倒进水后盆里水温度的最大值(每次计算时情况独立否则太简单了,不计倒水时的热损耗)。数据保证有解(即每次倒入水时总有办法使盆内水总体积为 V)。
  • 1n5×105,1V109,0ti109(1in),1viV(1in,v1=V)

题目分析

题意中既要按顺序倒入水又要倒出水,还要不停维护最大值,考虑到较大的数据范围,我们应使用单调队列进行求解。根据题意我们可以维护一些水的决策,具体每个决策有 v(体积)、t(温度) 两个属性值。

我们先考虑在“倒进”水之前要“倒出”的水决策。很明显它的 t 一定要尽可能低,否则倒出 t 更低的决策明显可以使总温度更高,因此更优。从而考虑维护 v 值单调递增的单调队列。每次“倒入”水(将当前决策入队)之前,为保持总体积恒定,根据“倒入”的水的 v 值不断“倒出”队头决策(出队),当队头决策无法完全“倒出”(即队头的 v 过大)时仅“倒出”一部分(保留队头决策,将它的 v 值减小一部分)。

然后我们再考虑倒入水的情况。首先将当前决策入队。由于当前决策的 t 值可能小于原来队尾决策的 t 值,违反了单调性,因此可以通过不停将原队尾与当前决策“混合”,降低队尾的 t 值,直到满足单调性为止。

值得一提的是,我们与其不断计算维护总温度值,不如利用物理学概念:热量 Q=mt(此题中是水,我们可以姑且认为 vm 等价),这样每次混合水的时候,可以直接得出 Q=Q1+Q2v=v1+v2,温度则只需要计算 t=Qm (此题中也可以表示为t=Qv),明显方便许多。

代码实现

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8;//避免浮点误差
int n,l=1/*队首*/,r/*队尾*/;
double V/*恒定总体积*/,q1[500010]/*单调队列中每个决策的体积*/,q2[500010]/*单调队列中每个决策的热量*/;
double del/*每次倒出的水*/,a1/*倒入水的温度*/,a2/*倒入水的体积*/,sm1/*总体积*/,sm2/*总热量*/;
int main()
{
scanf("%d%lf",&n,&V);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&a1,&a2);
while(sm1+a2>V)//当前体积加上倒入水的体积大于恒定体积就倒出队头
{
del=min(q1[l],sm1+a2-V);//倒出队头,倒不完就只倒一部分
sm1-=del;//减去队头体积
sm2-=del*q2[l];//减去队头热量
q1[l]-=del;//队头体积减去倒掉的部分
if(fabs(q1[l])<eps)//如果队头倒没了就出队
l++;
}
q2[++r]=a1;
q1[r]=a2;//倒入水,入队
sm1+=a2;//更新体积
sm2+=a2*a1;//更新热量
while(l<r&&q2[r-1]>q2[r])//维护单调性
{
r--;
q2[r]=(q2[r]*q1[r]+q2[r+1]*q1[r+1])/(q1[r]+q1[r+1]);//混合后的队尾温度
q1[r]+=q1[r+1];//混合后的体积
}
printf("%.7lf\n",sm2/sm1);//热量÷质量(体积)=温度
}
return 0;
}
posted @   Hadtsti  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示