#P3778. 第2题-视频播放时长
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 43
            Accepted: 19
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              百度
                                
            
                        
              时间 :2025年9月23日
                              
                      
          
 
- 
                        算法标签>贪心算法          
 
第2题-视频播放时长
解题思路
给定:
- 共 
n个视频,第i个视频长度为a_i; - 平台参数 
x:一次播放要被计数,至少观看min(a_i, x)秒; - 每个视频的播放次数 
v_i满足下界q_i与上界r_i; - 总播放次数为 
m(保证可行:∑q_i ≤ m ≤ ∑r_i)。 
一次有效播放带来的最少时长为 min(a_i, x),最多时长为 a_i。
因此在满足 q_i ≤ v_i ≤ r_i 且 ∑v_i = m 的前提下:
- 令 
c_i^L = min(a_i, x)。要使总时长最小,就把“额外的”播放次数尽量分配给c_i^L小的那些视频; - 令 
c_i^R = a_i。要使总时长最大,就把“额外的”播放次数尽量分配给c_i^R大的那些视频。 
做法(同一套框架,系数不同):
- 
先给每个视频分配下界
q_i,得到剩余可分配次数left = m - ∑q_i; - 
计算每个视频还能再加的上限
cap_i = r_i - q_i; - 
将视频按“单位播放贡献”的系数排序:
- 最小值 
L:按c_i^L从小到大; - 最大值 
R:按c_i^R从大到小; 
 - 最小值 
 - 
依次把
left分配给当前视频,分配量为add = min(left, cap_i),并增加时长add * 系数,直至left = 0。 
该贪心正确性可由交换论证:若把一次额外播放从更差的系数移动到更优的系数,会使目标更优,因此最优分配一定满足“尽量给最优系数”的顺序。
复杂度分析
- 排序占主导,时间复杂度 
O(n log n);一次线性分配O(n)。 - 只需保存若干数组与排序辅助结构,空间复杂度 
O(n)。 - 所有累加均可能很大,需使用 64 位整型。
 
代码实现
Python
# -*- coding: utf-8 -*-
# 题意实现函数放在外部;主函数只负责输入输出(ACM风格)
from typing import List, Tuple
def calc_min_total_time(n: int, x: int, m: int,
                        a: List[int], q: List[int], r: List[int]) -> int:
    """计算最小总观看时长"""
    # 单位时长系数为 min(a_i, x)
    coeff = [min(a[i], x) for i in range(n)]
    base = 0  # 先分配下界
    caps = []
    for i in range(n):
        base += q[i] * coeff[i]
        caps.append((coeff[i], r[i] - q[i]))  # (系数, 还能加的次数)
    left = m - sum(q)
    # 按系数从小到大贪心
    caps.sort(key=lambda t: t[0])
    res = base
    for c, cap in caps:
        if left <= 0:
            break
        add = min(left, cap)
        res += add * c
        left -= add
    return res
def calc_max_total_time(n: int, x: int, m: int,
                        a: List[int], q: List[int], r: List[int]) -> int:
    """计算最大总观看时长"""
    coeff = [a[i] for i in range(n)]  # 单位时长系数为 a_i
    base = 0
    caps = []
    for i in range(n):
        base += q[i] * coeff[i]
        caps.append((coeff[i], r[i] - q[i]))
    left = m - sum(q)
    # 按系数从大到小贪心
    caps.sort(key=lambda t: -t[0])
    res = base
    for c, cap in caps:
        if left <= 0:
            break
        add = min(left, cap)
        res += add * c
        left -= add
    return res
def main():
    # 输入:n x m
    n, x, m = map(int, input().split())
    a = list(map(int, input().split()))
    q = list(map(int, input().split()))
    r = list(map(int, input().split()))
    L = calc_min_total_time(n, x, m, a, q, r)
    R = calc_max_total_time(n, x, m, a, q, r)
    print(L, R)
if __name__ == "__main__":
    main()
Java
// ACM 风格,类名为 Main。读取输入,功能在外部静态函数里实现。
import java.io.*;
import java.util.*;
public class Main {
    // 计算最小总观看时长:单位系数为 min(a_i, x)
    static long calcMin(int n, int x, long m, int[] a, long[] q, long[] r) {
        long base = 0;
        long left = m;
        for (int i = 0; i < n; i++) left -= q[i];
        // (coef, cap)
        long[][] arr = new long[n][2];
        for (int i = 0; i < n; i++) {
            long coef = Math.min(a[i], x);
            base += q[i] * coef;
            arr[i][0] = coef;
            arr[i][1] = r[i] - q[i];
        }
        Arrays.sort(arr, new Comparator<long[]>() {
            public int compare(long[] p1, long[] p2) {
                return Long.compare(p1[0], p2[0]); // 从小到大
            }
        });
        long res = base;
        for (int i = 0; i < n && left > 0; i++) {
            long add = Math.min(left, arr[i][1]);
            res += add * arr[i][0];
            left -= add;
        }
        return res;
    }
    // 计算最大总观看时长:单位系数为 a_i
    static long calcMax(int n, int x, long m, int[] a, long[] q, long[] r) {
        long base = 0;
        long left = m;
        for (int i = 0; i < n; i++) left -= q[i];
        long[][] arr = new long[n][2];
        for (int i = 0; i < n; i++) {
            long coef = a[i];
            base += q[i] * coef;
            arr[i][0] = coef;
            arr[i][1] = r[i] - q[i];
        }
        Arrays.sort(arr, new Comparator<long[]>() {
            public int compare(long[] p1, long[] p2) {
                return Long.compare(p2[0], p1[0]); // 从大到小
            }
        });
        long res = base;
        for (int i = 0; i < n && left > 0; i++) {
            long add = Math.min(left, arr[i][1]);
            res += add * arr[i][0];
            left -= add;
        }
        return res;
    }
    public static void main(String[] args) throws Exception {
        // 使用 BufferedReader + StringTokenizer,兼顾可读性与性能
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int x = Integer.parseInt(st.nextToken());
        long m = Long.parseLong(st.nextToken());
        int[] a = new int[n];
        long[] q = new long[n];
        long[] r = new long[n];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) a[i] = Integer.parseInt(st.nextToken());
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) q[i] = Long.parseLong(st.nextToken());
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) r[i] = Long.parseLong(st.nextToken());
        long L = calcMin(n, x, m, a, q, r);
        long R = calcMax(n, x, m, a, q, r);
        System.out.println(L + " " + R);
    }
}
C++
// ACM 风格:主函数读写,功能写在外部函数里
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// 计算最小总观看时长:按 min(a_i, x) 从小到大分配
long long calc_min_total_time(int n, long long x, long long m,
                              const vector<long long>& a,
                              const vector<long long>& q,
                              const vector<long long>& r) {
    vector<pair<long long,long long>> vec; // (coef, cap)
    vec.reserve(n);
    long long base = 0;
    long long left = m;
    for (int i = 0; i < n; ++i) left -= q[i];
    for (int i = 0; i < n; ++i) {
        long long coef = min(a[i], x);
        base += q[i] * coef;
        vec.push_back({coef, r[i] - q[i]});
    }
    sort(vec.begin(), vec.end(), [](const auto& p1, const auto& p2){
        return p1.first < p2.first; // 从小到大
    });
    long long res = base;
    for (auto &p : vec) {
        if (left <= 0) break;
        long long add = min(left, p.second);
        res += add * p.first;
        left -= add;
    }
    return res;
}
// 计算最大总观看时长:按 a_i 从大到小分配
long long calc_max_total_time(int n, long long x, long long m,
                              const vector<long long>& a,
                              const vector<long long>& q,
                              const vector<long long>& r) {
    vector<pair<long long,long long>> vec; // (coef, cap)
    vec.reserve(n);
    long long base = 0;
    long long left = m;
    for (int i = 0; i < n; ++i) left -= q[i];
    for (int i = 0; i < n; ++i) {
        long long coef = a[i];
        base += q[i] * coef;
        vec.push_back({coef, r[i] - q[i]});
    }
    sort(vec.begin(), vec.end(), [](const auto& p1, const auto& p2){
        return p1.first > p2.first; // 从大到小
    });
    long long res = base;
    for (auto &p : vec) {
        if (left <= 0) break;
        long long add = min(left, p.second);
        res += add * p.first;
        left -= add;
    }
    return res;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    long long x, m;
    if (!(cin >> n >> x >> m)) return 0;
    vector<long long> a(n), q(n), r(n);
    for (int i = 0; i < n; ++i) cin >> a[i];
    for (int i = 0; i < n; ++i) cin >> q[i];
    for (int i = 0; i < n; ++i) cin >> r[i];
    long long L = calc_min_total_time(n, x, m, a, q, r);
    long long R = calc_max_total_time(n, x, m, a, q, r);
    cout << L << " " << R << "\n";
    return 0;
}
        题目内容
小明是一个自媒体从业者,他入驻的视频平台要用视频播放时长代替播放量,他对此感到忧虑,想要知道修改后自己视频播放时长的范围。
平台原来的政策为:当用户完整观看小明的某一视频或观看该视频超过 x 秒时,该视频的播放量增加 1 ,一位用户只能对某一视频贡献一次播放。
政策修改后:当用户完整观看小明的某一视频或观看该视频超过 x 秒时,该视频的播放时长增加对应时长,一位用户能对某一视频贡献的播放时长不超过视频长度。
小明一共发布了 n 个视频,其中第 i 个视频的长度为 a 秒。也就是说,对于第 i 个视频,若某一用户观看该视频至少 t=min(ai,x) 秒,则播放量加 1 ,而播放时长则增加 min(ai,t) 。
小明记得自己发布视频的总播放量为 m 次,但对每个视频,他只记得该视频的播放量在区间 [li,ri] 内。现在,请你帮小明算出他发布的视频的最短和最长总播放时长。
输入描述
第一行三个空格分隔的整数 n,x,m ,分别表示小明发布的视频数量,参数 x 的值和总播放量;
第二行 n 个空恪分隔的整数 ai ,第 i 个数表示第 i 个视频的长度;
第三行 n 个空格分隔的整数 li ,第 i 个数表示第 i 个视频的播放量下界;
第四行 n 个空恪分隔的整数 ri ,第 i 个数表示第 i 个视频的播放量上界。
1≤n≤105,1≤x,ai,li,ri≤106,∑li≤m≤∑ri
输出描述
输出一行两个空格分隔的整数 L,R ,表示小明发布视频的最短和最长总播放时长。
样例1
输入
2 5 8
4 7
3 3
9 9
输出
35 47