Codeforce Tea Tasting 二分 前缀和 差分 综合算法题

Codeforce Tea Tasting


分享一道炒鸡好的算法题目,结合了二分搜索,前缀和,差分等思想。

题目如下:

Tea taste
在这里插入图片描述

大家可以点击查看题目。

题目大概的意思是:有n种茶叶,同时有n个人去品尝,a[i]表示第i种茶还剩余多少,b[i]表示的是第i个人能喝多少茶,然后每次b[]数组向左移动一位,也就是第一个人喝完第一种茶就走人了,那接下来n-1个人继续喝n种茶

示意图
求解: 题目让我们求通过上述操作,每个人能够喝多少茶?

本题目考察了二分,前缀和,差分等重要思想,考察的十分综合。

思路如下:
下面我将a[] 简称为茶数组b[] 简称为人数组

  1. 我们观察到我们都是人数组都是从右往左移动,这代表着:每个人能喝多少种茶
    例如 :第i个人,能喝i种茶,第3个人能够3种茶,分别是1,2,3

  2. 那不妨我们换个思路:让茶移动

    在这里插入图片描述

  3. 我们发现:让茶移动,说明第i种茶,能够被第i~n个人喝到。

  4. 那我们可以求人数组的前缀和,通过求前缀和可以看到第i个人到第n个人一共喝了多少,此时开一个s[]数组去存储前缀和。

  5. 根据 3. 思路,我们要知道第i种茶能够被i-l个人喝完呢?我们要求l,那我们改怎么做呢? 这时候就需要二分去求解了。

  6. 二分求解完毕后,我们就知道了第i种茶,能够被第i~l个人喝完了。

  7. 然后我们再开两个数组 rx[]存储剩余的茶, q[]数组存储差分。

  8. 然后求q[]的前缀和 得到 第 i喝了多少次b[i]量的茶

  9. 通过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:
**加粗样式
**

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值