Codeforce Tea Tasting
分享一道炒鸡好的算法题目,结合了二分搜索,前缀和,差分等思想。
题目如下:
大家可以点击查看题目。
题目大概的意思是:有n
种茶叶,同时有n
个人去品尝,a[i]
表示第i
种茶还剩余多少,b[i]
表示的是第i
个人能喝多少茶,然后每次b[]
数组向左移动一位,也就是第一个人喝完第一种茶就走人了,那接下来n-1
个人继续喝n
种茶
求解: 题目让我们求通过上述操作,每个人能够喝多少茶?
本题目考察了二分,前缀和,差分等重要思想,考察的十分综合。
思路如下:
下面我将a[]
简称为茶数组
, b[]
简称为人数组
-
我们观察到我们都是人数组都是从
右往左
移动,这代表着:每个人能喝多少种茶
例如 :第i
个人,能喝i
种茶,第3个人能够3种茶,分别是1,2,3 -
那不妨我们换个思路:让茶移动
-
我们发现:让茶移动,说明第
i
种茶,能够被第i~n
个人喝到。 -
那我们可以求人数组的前缀和,通过求前缀和可以看到第
i
个人到第n
个人一共喝了多少,此时开一个s[]
数组去存储前缀和。 -
根据 3. 思路,我们要知道第
i
种茶能够被i-l
个人喝完呢?我们要求l
,那我们改怎么做呢? 这时候就需要二分去求解了。 -
二分求解完毕后,我们就知道了第
i
种茶,能够被第i~l
个人喝完了。 -
然后我们再开两个数组
rx[]
存储剩余的茶,q[]
数组存储差分。 -
然后求
q[]
的前缀和 得到第 i喝了多少次b[i]量的茶
-
通过
q[i] * b[i] + rx[i]
得到第i
个人喝了多少茶。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, m;
int a[N], b[N]; // a每种茶剩余量 b每种每个人能喝多少
int s[N]; // 表示第1-i个人一共可以喝多少的茶
int q[N]; // 倍数
int rx[N]; // 剩余
int ts; // 输入一共查询几次
signed main() {
cin >> ts;
while(ts --) {
cin >> n;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i];
q[i] = rx[i] = s[i] = 0;
}
// 求前缀和 s[] 表示第1-i个人一共可以喝多少的茶
for(int i = 1; i <= n; i ++ ) {
cin >> b[i];
s[i] = b[i] + s[i - 1];
}
// 二分出 i个
for(int i = 1; i <= n; i ++ ) {
int x = a[i]; // 第i种茶的剩余
int l = i, r = n; // 第i种茶可以被i-n个人喝
while(l < r) {
int mid = l + r + 1 >> 1;
// s[mid] - s[i - 1] 左边区域可以喝多少
if(s[mid] - s[i - 1] <= x) l = mid;
else r = mid - 1;
} // 找第i种茶最多支持第i个人--第r个人喝完
// 分类讨论
// 如果找到的l到i-1中间部分的茶量小于等于剩余茶量
// 这里每个人都能喝到自己b[]数组里面的茶
if(s[l] - s[i - 1] <= x) {
q[i] ++; // q[i] ++ 相当于i之后后面的全都全都喝了第i杯茶
q[l + 1] --; // q[i] -- 相当于j之后的全都没喝第i杯茶
rx[l + 1] += (x - (s[l] - s[i 1])); // 第l+1剩余茶量
}
else
{
// s[l] - s[i - 1] > x 茶喝完了
rx[i] += x; //
}
}
// 输出第i个人喝了多少
for(int i = 1; i <= n; i ++ )
{
q[i] += q[i - 1]; // 求前缀和 i-l 喝了 +c 的茶
cout << q[i] * b[i] + rx[i] << " \n"[i == n];
}
}
return 0;
}
看完代码之后,会有人疑问为什么要加上rx[i]
? rx[i]
是怎么来的?
这里我配合代码进行讲解:
通过之前二分的代码我们求出了i~l
,第i~l
个人,能够喝完或者是还有剩余i
种茶。
下面代码就是进行边界的判断:
if(s[l] - s[i - 1] <= x) {
q[i] ++; // q[i] ++ 相当于i之后后面的全都全都喝了第i杯茶
q[l + 1] --; // q[i] -- 相当于j之后的全都没喝第i杯茶
rx[l + 1] += (x - (s[l] - s[i 1])); // 第l+1剩余茶量
}
else
{
// s[l] - s[i - 1] > x 茶喝完了
rx[i] += x; //
}
}
说明
情况1:
情况2:
**
**