题目 求和 桐桐撒金币 【前缀和与差分】小码君的荣耀 投票箱 卡牌 暴躁的病人
求和
题目大意
给定 nnn 个整数 a1,a2,…,ana_1, a_2, \dots, a_na1 ,a2 ,…,an ,要求计算所有两两不同的乘积之和,即:
S=∑1≤i<j≤nai⋅ajS = \sum_{1 \le i < j \le n} a_i \cdot a_jS=∑1≤i<j≤n ai ⋅aj
例如,当 a=[1,2,3]a = [1, 2, 3]a=[1,2,3] 时:
S=1⋅2+1⋅3+2⋅3=11S = 1 \cdot 2 + 1 \cdot 3 + 2 \cdot 3 = 11S=1⋅2+1⋅3+2⋅3=11
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
本题目要求计算 ai⋅aja_i \cdot a_jai ⋅aj 的总和,满足 1≤i<j≤n1 \le i < j \le n1≤i<j≤n。
直接做法是使用两层循环枚举所有的 i,ji, ji,j,时间复杂度为 O(n2)O(n^2)O(n2),当 nnn 较大时会超时。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
我们可以利用数学变形将其优化为线性算法。
观察下面的展开形式:
S=a1⋅(a2+a3+⋯+an)+a2⋅(a3+⋯+an)+⋯+an−1⋅anS = a_1 \cdot (a_2 + a_3 + \dots + a_n) + a_2 \cdot (a_3 + \dots + a_n) + \dots + a_{n-1} \cdot a_nS=a1 ⋅(a2 +a3 +⋯+an )+a2 ⋅(a3 +⋯+an )+⋯+an−1 ⋅an
即:
S=∑i=1n−1ai⋅(∑j=i+1naj)S = \sum_{i=1}^{n-1} a_i \cdot \left( \sum_{j=i+1}^{n} a_j \right)S=∑i=1n−1 ai ⋅(∑j=i+1n aj )
因此,我们可以:
1. 先计算前缀和数组 prei=∑k=1iakpre_i = \sum_{k=1}^{i} a_kprei =∑k=1i ak ;
2. 对于每个 aia_iai ,它的贡献是 ai⋅(pren−prei)a_i \cdot (pre_n - pre_i)ai ⋅(pren −prei );
3. 将所有这些贡献累加得到答案。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度解析
* 计算前缀和:O(n)O(n)O(n);
* 枚举每个 iii 计算贡献:O(n)O(n)O(n);
* 总时间复杂度为 O(n)O(n)O(n);
* 空间复杂度为 O(n)O(n)O(n)(用于存储数组和前缀和)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
桐桐撒金币
题目大意
桐桐在一条公路上撒金币,每一公里都有一些金币。你需要处理 kkk 次询问,每次询问某一段区间 [l,r][l, r][l,r] 上金币的总和。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
本题本质是一个经典的区间和查询问题。
给定一个长度为 nnn 的数组 aaa,和 kkk 个区间 [l,r][l, r][l,r],每次要回答:
区间和=al+al+1+⋯+ar\text{区间和} = a_l + a_{l+1} + \dots + a_r区间和=al +al+1 +⋯+ar
直接每次遍历 [l,r][l, r][l,r] 会超时,最坏情况下时间复杂度 O(nk)O(nk)O(nk)。
我们考虑使用前缀和优化查询,将每次查询降为 O(1)O(1)O(1)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
使用前缀和数组 preprepre:
* 定义 pre[i]=a1+a2+⋯+aipre[i] = a_1 + a_2 + \dots + a_ipre[i]=a1 +a2 +⋯+ai
* 则有:
区间[l,r]的和=pre[r]−pre[l−1]\text{区间} [l, r] \text{的和} = pre[r] - pre[l - 1]区间[l,r]的和=pre[r]−pre[l−1]
实现步骤:
1. 输入数组并构建前缀和;
2. 对每个区间查询,使用前缀和快速计算答案。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度解析
* 构建前缀和数组:O(n)O(n)O(n);
* 每次查询使用 O(1)O(1)O(1),共 kkk 次,总计 O(k)O(k)O(k);
* 总时间复杂度为 O(n+k)O(n + k)O(n+k);
* 空间复杂度 O(n)O(n)O(n)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
投票箱
题目大意
有 NNN 个城市,总共 BBB 个投票箱。第 iii 个城市有 aia_iai 名选民。现需为每个城市分配至少一个投票箱,且每位选民必须在其城市被分配的投票箱中投票。每个投票箱可以服务一定数量的选民。
目标是通过合理分配投票箱,使得“单个投票箱服务的最大人数”尽可能小。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
这是一道最小化最大值的优化问题。
* 每个城市必须分至少一个箱子。
* 每个城市内,可以将人口均匀分配到多个箱子中,向上取整即为该城市所需投票箱数。
* 我们要找的是一个值 xxx,使得所有城市中,任意一个投票箱服务的人数不超过 xxx,且所需的总投票箱数量不超过 BBB。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
1. 二分答案
我们可以二分答案 xxx(即单个投票箱服务的人数),判断是否存在一个分配方案使得所有选民都能完成投票且不超过 BBB 个箱子。
2. 检查函数 CHECK(X)
对于当前猜测的最大投票箱负载 xxx,我们要判断是否可以在不超过 BBB 个投票箱的前提下覆盖所有人口:
* 每个城市 iii 至少需要 ⌈aix⌉\left\lceil \frac{a_i}{x} \right\rceil⌈xai ⌉ 个箱子;
* 累加所有城市所需的箱子数是否小于等于 BBB。
如果可以,说明 xxx 还可以继续减小;否则 xxx 太小了,不能满足要求。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度解析
* 每组数据最多有 555 组。
* 二分最多查找 log2(5×106)≈23\log_2(5 \times 10^6) \approx 23log2 (5×106)≈23 次;
* 每次判断 O(N)O(N)O(N),所以总复杂度约为 O(T⋅NlogM)O(T \cdot N \log M)O(T⋅NlogM),可以接受。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示
卡牌
题目大意
有 nnn 种卡牌,第 iii 种卡牌已有 aia_iai 张。还有 mmm 张空白卡牌可以涂写,但第 iii 种卡牌最多可以涂写 bib_ibi 张。现在你要尽可能多地组成“完整套牌”,即每套包含每种卡牌各一张。问:最多能组成多少套完整的卡牌?
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
每组成一套完整的卡牌,需要每种卡牌各一张。若某种卡牌数量不够,可使用空白卡牌进行补充,但不能超过该种牌可手写上限 bib_ibi ,总手写卡牌数量不能超过 mmm。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
使用二分答案来判断最多能组成多少套完整的卡牌:
1. 枚举可以组成的套数 xxx;
2. 对于每个种类的卡牌:
* 如果已有 ai≥xa_i \ge xai ≥x,无需补;
* 如果 ai<xa_i < xai <x,需要补 x−aix - a_ix−ai 张,且不能超过 bib_ibi ;
3. 所有需要补的数量不能超过总空白卡牌数 mmm。
为了保证复杂度最优,采用倒序线性判断(f_20() 函数)或 二分查找(f() 函数)都可以。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
时间复杂度分析
* 最坏情况每次判断 O(n)O(n)O(n);
* f_20() 复杂度为 O(n⋅A)O(n \cdot A)O(n⋅A),其中 A=m/n+max(ai)A = m/n + \max(a_i)A=m/n+max(ai );
* f() 为 O(n⋅logA)O(n \cdot \log A)O(n⋅logA),推荐在数据范围较大时使用;
* 对于 n≤2×105n \le 2 \times 10^5n≤2×105,m≤5×106m \le 5 \times 10^6m≤5×106 是可接受的。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
暴躁的病人
题目大意
有 NNN 个病房,坐标分别为 x1,x2,…,xNx_1, x_2, \dots, x_Nx1 ,x2 ,…,xN ,共 CCC 个病人。
需要将这 CCC 个病人安置在病房中,使得任意两个相邻病人的距离尽可能大。
求这个“最大化的最小相邻距离”。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题意分析
* 这是一个典型的最大化最小间距问题,类似于“放置C个元素,使它们之间的最小距离最大”。
* 约束:
* 每个病人占一个病房;
* 病房的坐标可能无序,需要先排序;
* 目标是找到一个最小距离 ddd,使得能把 CCC 个病人安排进病房,使得任意两人之间距离至少 ddd。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解题思路
1. 先排序
将病房坐标 xix_ixi 按升序排序。
2. 二分距离
用二分法枚举最小距离 midmidmid:
* 判定函数 check(mid) 判断能否安排 CCC 个病人在病房中,使得任意两人距离至少为 midmidmid。
* 方法:
* 第一个病人安排在第一个病房(下标1);
* 遍历后续病房,若当前病房和上一个安排病人的病房距离 ≥mid\ge mid≥mid,则安排一个病人;
* 统计安排了多少病人,若 ≥C\ge C≥C,则说明可以用 midmidmid 距离安排,返回真。
3. 二分调整
* 初始左右边界:l=0l=0l=0,r=max(x)−min(x)r= \max(x) - \min(x)r=max(x)−min(x)(最大坐标差)。
* 若 check(mid) 成功,更新答案,尝试更大距离 l=mid+1l=mid+1l=mid+1;
* 否则缩小距离 r=mid−1r=mid-1r=mid−1。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码演示