AtCoder Beginner Contest 005

A

输入正整数 \(x, y\) 。输出 \(\lfloor \frac{y}{x} \rfloor\)

B

输入 \(a_1, a_2, \cdots, a_n\) 。输出 \(min\{a_i\},(1 \leq i \leq n)\)

C

题意

一个章鱼烧只能留存 \(T\) 秒。
\(N\) 个章鱼烧的顺序出炉时间是 \(a_1, a_2, \cdots, a_N\)
\(M\) 个顾客顺序到达的时间是 \(b_1, b_2, \cdots, b_M\)
顾客达到时若恰好不能买章鱼烧就会离去,询问是否所有顾客都能买到章鱼烧。

题解

这是比较明显的先入先出,由于后入的有更多购买章鱼烧的机会,所以贪心让先入的购买。

根据先入先出的性质,对顾客维护一个普通队列。枚举章鱼烧的出炉时间。

每次把失去耐心离开的顾客全部出队,此时若队首的顾客此时已经到达,则让他购买这个章鱼烧,答案加一。

时间复杂度 \(O(n + m)\)

view

另一种更直观但复杂度更高的解法。

我们可以让源点 \(s\) 向所有章鱼烧连一条容量为 \(1\) 的边,让所有顾客向汇点 \(t\) 连一条容量为 \(1\) 的边。
对于任意一个章鱼烧,查询所有顾客,从出炉时间到过期时间来的所有顾客,都连接一条容量为 \(1\) 的边。
建图复杂度 \(O(n^{2})\)

然后用 \(Dinic\) 求最大流,在二分图中的时间复杂度只需要 \(O(n \sqrt{m}) = n^{2}\)

这题的 PRO 版本

将这题的 \(T\) 改成 \(T_1, T_2, \cdots, T_m\) 表示每个顾客的等待时间。那么最终有多少顾客可以买到章鱼烧?

暴力的做法?

依旧可以选择 \(O(n^{2})\) 的网络流二分图做法。

更快的做法?

首先它没有了先入先出的性质。

考虑 \(T_i\) 不同,于是不再有先入先出的性质,不能直接使用队列。

观察到:每个顾客覆盖了一段 \([b_i, b_i + T_i]\) 的区间。

依旧考虑贪心将章鱼烧分配给最早离开的人,则在线算法实现很困难,考虑离线算法。

不妨重定义为 \(([\{st_i, 0\}, \{ed_i + 1, 1\}))\) ,表示左开右闭区间和区间种类。

\(2m\) 个区间端点按照升序排序,则构成了一个维度的偏序。左闭区间为加入点,右开区间为删除点。

\(n\) 个章鱼烧的出炉点作为询问点。

于是这就是一个一维偏序的离线扫描问题。

首先将 \(2m + n\) 个时间离散化,再对时间开树状数组,利用区间修改维护每个点被多少顾客区间覆盖,记这个值为 \(val\)

设能够被购买的章鱼烧为 \(ans\) ,它在每个询问点所在的区间内最多增加 \(val\) 次。

动态维护 \(cnt\) 为购买了当前区间内章鱼烧的顾客数量,删去一个点时 \(cnt = max(cnt - 1, 0)\)

遇到一个询问点时,若 \(cnt < val\) ,则 \(cnt = cnt + 1\)\(ans = ans + 1\)

最后 \(ans\) 即能买到章鱼烧的顾客的最大数。

另一个题。

一开始因为翻译问题会错了题意。把“一个章鱼烧能留存 \(t\) 秒”理解成了“卖一个章鱼烧需要 \(t\) 秒”。
这样依旧会是一个有意思的题。

解法是:

  1. \(a_i\) 作为左括号,\(b_i\)作为右括号,将它们按时间排序。

那么从第一个括号起,到最后一个右括号,这段括号序列一定得是个合法的括号序列。

只有这样每个顾客都有可能性买到章鱼烧。问题在于一个顾客到场时,章鱼啥的数量是够的,但是此时老板正在卖给别人。那么这个顾客不会等。

由于卖卖章鱼烧的行为如果能够发生,是在一个顾客达到时立刻发生。那么只需要:

  1. 每个相邻两个右括号的差值 \(\geq t\)

时间复杂度 \(O(n)\)

D

diffculty 1447

题意

章鱼烧摸具是 \(N \times N\) 的,第 \((i, j)\) 个位置可以放置一个章鱼烧,烤出的美味度是 \(a_{i, j}\)
存在 \(Q\) 个员工,每个员工一次能烤的章鱼烧上限为 \(P_1, P_2, \cdots, P_Q\)
烤一次章鱼烧一定要在摸具中选择一个矩形区域,且每个位置都要放满章鱼烧。
询问每个员工一次能烤出的章鱼烧的美味度之和最大值。

\(N \le 100\)

题解

首先使用二维前缀和处理,以便能够 \(O(1)\) 回答任意一个矩形的权值和。用时 \(O(N^{2})\)
维护 \(f[i]\) 为烤 \(i\) 个章鱼烧能够得到的最大美味度之和,
然后 \(O(N^{4})\) 枚举矩形的四个边界计算权值和,设面积为 \(S\) ,更新 \(f[S]\)

最后让 \(g[i] = max(f[i - 1], g[i])\)\(g[i]\) 代表烤不超过 \(i\) 个章鱼烧能够得到的最大美味度之和。

注意到 \(P_i > n \times n\) 没有意义,回答时让 \(P_i\)\(n \times n\)\(min\)

总时间复杂度 \(O(N^{4})\)

view
	int n; std::cin >> n;
    const int m = n;
    std::vector<std::vector<int> > g(n + 1, std::vector<int>(m + 1));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            std::cin >> g[i][j];
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            g[i][j] += g[i][j - 1];
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            g[i][j] += g[i - 1][j];
        }
    }

    std::vector<int> f(n * m + 1, 0);
    for (int up = 1; up <= n; up++) {
        for (int down = up; down <= n; down++) {
            for (int left = 1; left <= m; left++) {
                for (int right = left; right <= m; right++) {
                    int S = (down - up + 1) * (right - left + 1);
                    f[S] = std::max(f[S], g[down][right] - g[up - 1][right] - g[down][left - 1] + g[up - 1][left - 1]);
                }
            }
        }
    }
    for (int i = 1; i <= n * m; i++)
        f[i] = std::max(f[i], f[i - 1]);
    
    int Q; std::cin >> Q;
    for (int i = 0; i < Q; i++) {
        int x; std::cin >> x;
        x = std::min(x, n * m);
        std::cout << f[x] << "\n";
    }
posted @ 2024-07-17 12:21  03Goose  阅读(38)  评论(0)    收藏  举报