1 条题解
-
0
一、题意重述
给定两个长度为 的数组 和 ,可以任意重排数组 ,求
的最大值,其中 是 的一个排列。
随后有 次操作:
- :将 加
- :将 加
要求输出初始答案 + 每次操作后答案,共 个结果,答案对 取模。
数据范围:
二、核心结论:最优配对策略
要最大化 ,经典贪心策略为:
- 将数组 从小到大排序得到
- 将数组 从小到大排序得到
- 按相同下标配对:
此时
证明简述: 排序后两两配对是使 乘积最大的唯一最优方案,任何交叉配对都会让至少一对 变小,从而乘积更小。
三、整体思路
-
预处理
- 复制 得到排序数组
- 对 分别升序排序
- 计算初始乘积
-
动态维护答案 每次操作只会单点增加 或 :
- 该元素在排序数组 或 中的位置可以用二分快速定位
- 若该位置原来满足 由它贡献,则更新答案:乘旧值的逆元,再乘新值
- 直接将排序数组中对应位置的值 ,保持数组有序(因为只加 ,不会破坏有序性)
-
模运算与逆元 因为要动态除以某个数,使用费马小定理求逆元:
四、标程关键细节解析
1. 快速幂求逆元
int qpow(int a, int x = MOD - 2) { int res = 1; for (; x; x >>= 1, a = 1ll * a * a % MOD) if (x & 1) res = 1ll * res * a % MOD; return res; }- 用途:计算 在模 下的乘法逆元
- 原理: 是质数,
2. 初始答案计算
sort(c + 1, c + n + 1); sort(d + 1, d + n + 1); for (int i = 1; i <= n; ++i) res = 1ll * res * min(c[i], d[i]) % MOD;- 是 的排序版, 是 的排序版
- 按位配对求 并累乘得到初始最大乘积
3. 单点修改维护
以修改 为例:
int p = upper_bound(c + 1, c + n + 1, a[x]) - c - 1; if (c[p] < d[p]) res = 1ll * res * qpow(c[p]) % MOD * (c[p] + 1) % MOD; ++a[x], ++c[p];- 二分定位
upper_bound找到 在有序数组 中的位置 - 判断是否影响答案 只有当 时,,修改 才会改变乘积
- 更新答案$$res = res \times c_p^{-1} \times (c_p+1) \bmod MOD $$
- 同步更新 原数组 与排序数组 同时
修改 逻辑完全对称。
五、复杂度分析
- 排序:
- 每次操作:二分 ,其余
- 总复杂度:
- 完全满足 的时限要求
六、正确性说明
- 每次只 ,排序数组仍保持有序,无需重新排序
- 二分能精准定位到原元素在排序数组中的位置
- 仅当该位置是 贡献者时才更新答案,保证结果正确
- 模运算全程使用 避免溢出,保证计算安全
七、标程运行流程(以样例1为例)
样例输入:
3 4 1 1 2 3 2 1初始:
- 排序后
- 依次为 ,乘积
后续操作依次修改 ,程序动态维护乘积,最终输出:
2 3 3 6 6与样例完全一致。
- 1
信息
- ID
- 6609
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者