• 【做题】arc072_f-Dam——维护下凸包


    题意:有一个容量为(L)的水库,每天晚上可以放任意体积的水。每天早上会有一定温度和体积的水流入水库,且要保证流入水之后水的总体积不能超过(L)。令体积分别为(V_1,V_2),温度分别为(t_1,t_2)的水混合后的温度为(frac {V_1 * t_1 + V_2 * t_2} {V_1 + V_2})。初始水库为空。现给出(n)天流入水的体积和温度,分别最大化每一天中午水库满容量时的水温。
    (n <= 500000, space L,t <= 10^9,space V <= L)

    显然记录水温时直接记录温度和体积的乘积,这样混合时直接相加就可以了。这个混合满足结合律。
    那么,我们可以把水表示为二维平面上的向量,(x)坐标为体积,(y)坐标为温度和体积的乘积。那么,温度就等于它的斜率。我们贪心一下,如果新加入的水导致水温下降,那么一定会在新加入水后再排水。否则,便可以在新加入水之前排水。这启发我们维护一个下凸包。如果某一天晚上是可以排水的,那么就不先与后面的水混合。否则便把两个向量相加。具体排水时放出斜率较小的水,流入水时不断与现有的水合并直到斜率大于之前的水为止。最大化第(i+1)天水的温度,就是最大化第(i)天放水后的水温,故我们直接从第(i)天的决策更新到第(i+1)天的决策,还可以保证新的决策时刻满足水的体积不超过(L)
    时间复杂度(O(n))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 500010;
    typedef double db;
    const db eps = 1e-8;
    inline int judge(db x) {
      return x > -eps ? x > eps ? 1 : 0 : -1;
    }
    struct point {
      db x,y;
      point(db x_=0,db y_=0): x(x_), y(y_) {}
      db operator * (const point& a) const {
        return x * a.y - y * a.x;
      }
      point operator + (const point& a) const {
        return point(x + a.x, y + a.y);
      }
      point operator - (const point& a) const {
        return point(x - a.x, y - a.y);
      }
      void operator += (const point& a) {
        *this = *this + a;
      }
      void operator -= (const point& a) {
        *this = *this - a;
      }
    } q[N];
    int n,cap,t,v,l=1,r;
    point cur,tmp;
    int main() {
      scanf("%d%d",&n,&cap);
      scanf("%d%d",&t,&v);
      printf("%d.00000000
    ",t);
      q[++r] = point(v,1.0 * t * v);
      cur += point(v,1.0 * t * v);
      for (int i = 2 ; i <= n ; ++ i) {
        scanf("%d%d",&t,&v);
        db a = v;
        while (judge(a - q[l].x) >= 0)
          a -= q[l].x, cur -= q[l++];
        cur -= q[l];
        q[l].y *= (q[l].x - a) / q[l].x;
        q[l].x -= a;
        cur += q[l];
        tmp = point(v,1.0 * t * v);
        while (l <= r && judge(q[r] * tmp) <= 0)
          tmp += q[r], cur -= q[r--];
        if (judge(tmp.x - cap) > 0) {
          tmp.y *= cap / tmp.x;
          tmp.x = cap;
        }
        q[++r] = tmp;
        cur += tmp;
        printf("%.8lf
    ",cur.y / cur.x);
      }
      return 0;
    }
    

    小结:在贪心的基础上维护下凸包(或单调队列),从而容易转移。感觉自己做起来还有难度。
  • 相关阅读:
    和为S的两个数字
    和为S的连续正数序列
    两个链表的第一个公共结点
    删除链表中重复的结点
    常用开发工具的安装(JDK、IDEA、Tomcat、Maven、Mysql和Nodepad++)——实习日志7.10
    蓄水池取样(转)
    prepare statement
    ProxySQL Getting started
    架构收录
    服务开机自启动
  • 原文地址:https://www.cnblogs.com/cly-none/p/9205674.html
Copyright © 2020-2023  润新知