• 洪水题解


    【洪水】题解

    题目大意

    (n)个水库,每个水库有一个容量上限(a_i),第(i)个水库到第(i+1)个水库有一个流量上限(b_i),最后一个水库流入大海。且每秒会有(F)流入第(1)个水库,问水库多久会溢出,不会溢出输出(-1)

    对于n小于4的数据,直接模拟即可

    对于所有水库容量相同,河道一样宽的数据

    因为所有水库都相同,所以除第一个水库外,其余水库都是水流进来多少就流出去多少,不需要蓄水

    所以当前答案就是(frac{a_1}{F-b_1})

    如果(Fle b_1),输出(-1)

    满分做法

    考虑贪心

    我们肯定是要让尽可能多的水往后流,这点是非常清楚的

    而且水库溢出的条件其实就只有第一个水库溢出(为什么下面讲)

    如果第(1)个水库没有满,那么我们就可以一直装到满为止。

    如果中间有个水库已经满了,我们就可以减少流向这个水库的水,将多余的水存在前面的水库,直至第一个水库

    这么一来,我们就得到了一种比较优秀的做法

    • 我们先算出每个水库群的净流入(因为越到后面流量肯定是单调不升的,其实就是个前缀最小值)

    • 然后我们找到第一个满的水库群(i),将其净流入全部存入(i-1)个水库群(就是让第(i-1)个水库群的净流量(+)(i)个水库群的净流量,一直这样到(1)号水库群为止)

    这里的净流入和水库群的定义是这样的

    水库群:有多个连续的水库组成,这几个水库里的水基本可以任意分配

    要怎么样才能满足水库群?

    设流入这个水库群的流量为(F),往右找到第一个流量(<F)的水库,这些水库就是一个水库群

    我们计入水库群的容量为水库群中所有水库容量之和,净流入为这个水库群的总流入-总流出

    净流入:水库群中自然状况下每秒必须流入的水量

    画张图

    无标题.png

    显然,当这些水库中没有任何一个水库群时,那么水便可以一直从头流到尾,输出(-1)

    显然,当一个水库群满时,我们需要将此水库群的净流入修改为(0)

    那么每秒消失的水去哪里了呢?我们将前一个水库群的净流入(+=)这个水库的净流入,就是要让前一个水库群帮这个水库群分担压力

    直到第一个水库群,没有所谓的前一个水库群,此时水库终于溢出

    如此,此题完结

    #include <bits/stdc++.h>
    #define int long long
    using namespace std ;
    const int MAXN = 3e5 + 5 ;
    int n , F ;
    int a[ MAXN ] , b[ MAXN ] ;
    int sum[ MAXN ] , t[ MAXN ] , tot = 1 ; // 水库群的容量,净流入和水库群的数量
    inline int read () {
        int tot = 0 , f = 1 ; char c = getchar () ;
        while ( c < '0' || c > '9' ) { if ( c == '-' ) f = -1 ; c = getchar () ; }
        while ( c >= '0' && c <= '9' ) { tot = tot * 10 + c - '0' ; c = getchar () ; }
        return tot * f ;
    }
    signed main () {
        // freopen ( "ex_flood2.in" , "r" , stdin ) ;
        // freopen ( "B.out" , "w" , stdout ) ;
        n = read () ; F = read () ;
        for ( int i = 1 ; i <= n ; i ++ )
            a[ i ] = read () ;
        for ( int i = 1 ; i <= n ; i ++ )
            b[ i ] = read () ;
        for ( int i = 1 ; i <= n ; i ++ ) {
            if ( b[ i ] < F ) { //前缀最小值,当前的流量
                // cout << F - b[ i ] << endl ;
                t[ tot ] = F - b[ i ] ; sum[ tot ++ ] += a[ i ] ; F = b[ i ] ; //计算净流入
    
            }
            else sum[ tot ] += a[ i ] ;//水库群容量上升
        } tot -- ;
        if ( tot == 0 ) { //没有任何一个水库群
            printf ( "-1
    " ) ;
            return 0 ;
        }
        int pos ;
        // cout << pos << endl ;
        double ans = 0 ;
        while ( 1 ) {
            double minx = 9999999999999 ;
            for ( int i = 1 ; i <= tot ; i ++ ) {
                if ( t[ i ] == 0 ) continue ;//防止除0而RE
                if ( minx > (double) sum[ i ] / (double) t[ i ] ) { //找到第一个溢出的水库群
                    minx = (double) sum[ i ] / (double) t[ i ] ;
                    pos = i ;//记录下标
                }
            }
            for ( int i = 1 ; i <= n ; i ++ ) sum[ i ] -= t[ i ] * minx ; //因为这个水库群装满需要minx秒,所以其他水库群的容量也要减少
            ans += minx ;//记录答案
            if ( pos == 1 ) break ; //做到第一个水库就不能往前继续推了
            // cout << ans << " " << pos << endl ;
            t[ pos - 1 ] += t[ pos ] ;//分担压力
            t[ pos ] = 0 ;//净流量降为0
        }
        printf ( "%.3lf
    " , ans ) ;
        return 0 ;
    }
    

    感觉这题还能用二分答案+乱搞秒掉,有兴趣的可以试试看

  • 相关阅读:
    <转载>大白话系列之C#委托与事件讲解(二)
    <转载>C# 类型基础
    <转载>大白话系列之C#委托与事件讲解(三)
    <转载>大白话系列之C#委托与事件讲解大结局
    <转载>C#中父窗口和子窗口之间实现控件互操作
    <转载>C# 中的泛型
    <转载>C# 中的委托和事件
    mailto的用法
    计算器
    终于搞清楚了这句代码的意思
  • 原文地址:https://www.cnblogs.com/hulean/p/13489147.html
Copyright © 2020-2023  润新知