题解:洛谷 P10188 Milk Exchange
【题目来源】
洛谷:P10188 [USACO24FEB] Milk Exchange B - 洛谷
【题目描述】
Farmer John 的 \(N\)(\(1\le N\le 2\cdot 10^5\))头奶牛排成一圈,使得对于 \(1,2,\ldots,N−1\) 中的每个 \(i\),奶牛 \(i\) 右边的奶牛是奶牛 \(i+1\),而奶牛 \(N\) 右边的奶牛是奶牛 \(1\)。第 \(i\) 头奶牛有一个容量为整数 \(a_i\)(\(1\le a_i\le 10^9\))升的桶。所有桶初始时都是满的。
每一分钟,奶牛都会根据一个字符串 \(s_1s_2\ldots s_N\)
传递牛奶,该字符串仅由字符 L 和 R 组成。当第 \(i\) 头奶牛至少有 \(1\) 升牛奶时,如果 \(s_i=\texttt{L}\),她会将 \(1\) 升牛奶传递给她左边的奶牛,如果 \(s_i=\texttt R\) 则传递给右边的奶牛。所有交换同时发生(即,如果一头奶牛的桶是满的,送出一升牛奶,但也收到一升,则她的牛奶量保持不变)。如果此时一头奶牛的牛奶量超过 \(a_i\),则多余的牛奶会损失。
FJ 想要知道:经过 \(M\) 分钟(\(1\le M\le 10^9\))后,所有奶牛总共还余下多少牛奶?
【输入】
输入的第一行包含 \(N\) 和 \(M\)。
第二行包含一个字符串 \(s_1s_2\ldots s_N\),仅由字符 L 或 R 组成,表示每头奶牛传递牛奶的方向。
第三行包含整数 \(a_1,a_2,\ldots,a_N\),为每个桶的容量。
【输出】
输出一个整数,为 \(M\) 分钟后所有奶牛总共余下的牛奶量。
注意这个问题涉及到的整数可能需要使用 64 位整数型(例如,C/C++ 中的 long long )。
【输入样例】
3 1
RRL
1 1 1
【输出样例】
2
【核心思想】
- 问题分析:\(N\) 头奶牛围成一圈,每头奶牛有容量 \(a_i\) 的桶,初始全满。每分钟,所有奶牛同时按照方向字符串 \(s\)(
L向左,R向右)传递 1 升牛奶(若自己有奶),同时可能收到 1 升。若某头牛收到后超过容量,溢出损失。求经过 \(M\) 分钟(\(M \le 10^9\))后,所有桶中剩余牛奶总量。
关键洞察:由于传递是同时且同步的,且每头牛每轮至多给出 1 升、收到 1 升,因此只有方向模式中出现 R 后紧接 L(即 RL)的位置才会产生溢出。因为牛奶会沿 R 方向汇聚,再沿 L 方向汇聚,最终在 RL 交界处堆积并溢出。其余位置的牛奶只是在循环流动,不会损失(若首尾衔接的 L...R 模式,则因为围成环,会形成稳定流,无净损失)。因此,只需找出所有 RL 交界点,计算每个交界点在 \(M\) 分钟内损失的牛奶量,从初始总量中扣除。
-
算法选择:
- 模拟 + 数学:不逐分钟模拟,而是分析 “溢出结构”。每个
RL交界点(奶牛 \(i\) 方向为R,奶牛 \((i+1)\bmod N\) 方向为L$)会成为一个“汇点”,左右两侧连续同向的奶牛会向该汇点输送牛奶。经过 $t$ 分钟,从汇点左侧的连续R段和右侧的连续L段中,每头牛会向汇点方向输送 $t$ 升牛奶(但受限于自身容量)。因此,每个RL结构的溢出量为:左侧连续R段所有奶牛的 $\min(a_j, M)$ 之和 + 右侧连续L段所有奶牛的 $\min(a_j, M)$ 之和,再与 $M$ 取较小值(因为每分钟该结构最多溢出 1 升)。总损失为所有RL` 结构损失之和。
- 模拟 + 数学:不逐分钟模拟,而是分析 “溢出结构”。每个
-
关键步骤(基于代码逻辑):
- 读入:\(N, M\),方向字符串 \(s\),容量数组 \(a_i\)。
- 计算初始总量:
ans = sum(a_i)。 - 标记溢出结构:遍历 \(i\) 从 0 到 \(N-1\),若 \(s[i] == 'R'\) 且 \(s[(i+1)\%N] == 'L'\),则标记
L[i] = true(该位置是结构起点,即R的位置)和R[(i+1)%N] = true(该位置是结构终点,即 `L$ 的位置)。 - 计算每个结构的溢出损失:
- 若
L[i] == true(即位置 \(i\) 是某个RL中的R),则向左(逆时针)扫描连续R段,累加该段所有奶牛的 \(\min(a_j, M)\) 到sum。 - 若
R[i] == true$(即位置 $i$ 是某个RL中的L),则向右(顺时针)扫描连续L段,累加该段所有奶牛的 $\min(a_j, M)$ 到sum`。 - 从
ans中减去min(sum, (long long)M),因为每个结构每分钟最多溢出 1 升。
- 若
- 输出
ans。
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),线性扫描并处理连续段。
- 空间复杂度:\(O(N)\),存储数组和标记。
-
模拟 + 数学的核心思想:
- 识别稳定流:在环形排列中,方向相同的连续段形成“流”。只有
R段后接L段的位置(RL边界)会产生牛奶堆积和溢出,因为牛奶从两侧流向该边界。相反,L后接R的边界 (LR) 会产生分流,不会溢出。 - 每轮损失上限:每个
RL边界每分钟最多损失 1 升牛奶(因为只有边界处的牛会收到来自两边的各 1 升,而自身给出 1 升,净增 1 升,若已满则溢出 1 升)。因此总损失 = 边界数 × 时间,但受限于各段奶牛的总容量(因为如果某段奶牛被掏空,则不再有牛奶流向边界)。 - 处理容量限制:对于左侧
R段,每头牛 \(j\) 在 \(M\) 分钟内最多贡献 \(\min(a_j, M)\) 升牛奶(因为每分钟输出 1 升,\(a_j\) 升后自桶空)。右侧L段同理。因此边界可损失的总量为这些贡献之和,与 \(M\) 取较小值。 - 适用场景:适用于环形同步传输、容量有限、问长时间后总量损失的模拟问题。核心是找出“汇聚点”,并利用时间与容量的最小值计算损失。
- 识别稳定流:在环形排列中,方向相同的连续段形成“流”。只有
【解题思路】


【算法标签】
普及+ #数学
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, m;
long long a[200005], ans;
string s;
bool L[200005], R[200005];
//L[i]=true表示点i为某个溢出结构的起点
//R[i]=true表示点i为某个溢出结构的终点
int main()
{
cin >> n >> m;
cin >> s;
for (int i=0; i<n; i++) {
cin >> a[i];
ans += a[i];
}
//第一步:从左到右找出字符串s中所有的溢出结构RL,并标记其起点和终点
for (int i=0; i<=n-1; i++) {
if (s[i]=='R' && s[(i+1)%n]=='L') {
L[i]=true;
R[(i+1)%n] = true;
}
}
//第二步:从左到右计算每个溢出结构导致的牛奶溢出
for (int i=0; i<n; i++) {
long long sum = 0; // 每个溢出结构溢出的牛奶数量
if (L[i]==true) { // i号桶为某个溢出结构的起点
int j=(i-1+n)%n; // 记录i号桶左边的编号j
while (s[j]=='R') {
sum += a[j];
j = (j-1+n)%n; // 继续向左
}
}
if(R[i]==true) { // i号桶为某个溢出结构的终点
int j = (i+1)%n; // 记录i号桶右边的编号j
while (s[j]=='L') {
sum += a[j];
j = (j+1)%n; // 继续向右
}
}
ans -= min(sum, (long long)m);
}
cout << ans << endl;
return 0;
}
【运行结果】
3 1
RRL
1 1 1
2
浙公网安备 33010602011771号