【AtCoder arc072_f/集训队作业】 Dam

【AtCoder arc072_f/集训队作业】Dam

所以说思维还是很重要的啊

题意

你有一个初始为空的水坝,每日都会有温度为 ti ,体积为 vi 的水流入。为使总水量不超过常数 L ,你每晚都可以放走一些水,使明日早晨的水可以全部流入。若水的温度不会随时间变化,只会受新流入的水影响,输出1n日中午在水坝是满的的条件下温度的最大值。满足第一天水坝一定会满。

分析

我曾尝试往构造单调队列上想,最后在单调队列上二分答案,但这样在遇到某日温度骤减的情况时难以处理。在观摩了别人的代码后,才意识到这题根本没有那么复杂。
我们断言能把前 i 天的注水在最优策略下等价变换为i天的注水(表现为单调队列),同时满足:
1. 这些天的总注水量为 L
2.被注入的水的温度随日期递增。
我们归纳证明:
1. 第一日显然可以
2. 我们要在i-1’天中选择若干天放水。因为 t 最大的同时tv也达到最大,而放水的 v 是确定的,所以放水时t要尽量小,故尽早放水即可(表现为队列 pop_front ),此时变为 i1′′ 天的注水。
再维护单调性:若第 i 日注水水温不为当前最大,由贪心可知,第i1′′天不放水一定不比放水劣,意味着我们可以将第 i1′′ 天的注水和第 i 天的注水合并(表现为pop_back),重复该过程直到第 i 日注水水温为当前最大,则 i 天的注水构造完成( push_back )。
根据最优性,将 i 日的注水混合后的温度即为第 i <script type="math/tex" id="MathJax-Element-33">i</script>日的答案。

AC代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define re_ return
#define in_ inline
#define op_ operator
#define inc(l, i, r) for(i=l; i<r; ++i)
typedef long long ll;
typedef double db;

struct wat
{
    db t; int v;
    wat(): t(0), v(0){}
    wat(db a, int b): t(a), v(b){}
    in_ wat op_+ (wat a)
    {re_ v+a.v? wat((t*v+a.t*a.v)/(v+a.v), v+a.v): wat();}
    in_ wat& op_+= (wat a)
    {re_ *this=*this+a;}
    in_ wat op_- (wat a)
    {re_ v^a.v? wat((t*v-a.t*a.v)/(v-a.v), v-a.v): wat();}
    in_ wat& op_-= (wat a)
    {re_ *this=*this-a;}
    in_ void wr(char* a)
    {printf("(%.6lf, %d)%s", t, v, a);}
};

const int mxn=1<<19;

int n, v0; wat q[mxn];

int main()
{
    int s, t; wat a, b;//b维护水坝中的所有水
//    a.wr("\n");
    scanf("%d%d", &n, &v0);
    for(s=t=0; n--;)
    {
        scanf("%lf%d", &a.t, &a.v);
        for(b+=a; b.v>v0;)//放水
            if(q[s].v<=b.v-v0) b-=q[s++];
            else q[s].v-=b.v-v0, b-=wat(q[s].t, b.v-v0);
        printf("%lf\n", b.t);
        for(;s<t && q[t-1].t>=a.t; a+=q[--t]);//合并所注水
        q[t++]=a;
    }
    re_ 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值