import java.util.*; import java.io.*; public class Main { static class Function { int k, a; Function(int k, int a) { this.k = k; this.a = a; } } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); int T = Integer.parseInt(br.readLine().trim()); while (T-- > 0) { String[] line = br.readLine().split(" "); int n = Integer.parseInt(line[0]); int l = Integer.parseInt(line[1]); int r = Integer.parseInt(line[2]); long S = 0; List<Function> list = new ArrayList<>(); for (int i = 0; i < n; i++) { line = br.readLine().split(" "); int k = Integer.parseInt(line[0]); int a = Integer.parseInt(line[1]); int b = Integer.parseInt(line[2]); S += b; if (k == 0) { S += Math.abs(a); } else { list.add(new Function(k, a)); } } int m = list.size(); if (m == 0) { bw.write(S + "\n"); continue; } Collections.sort(list, (f1, f2) -> { long left = (long) (-f1.a) * f2.k; long right = (long) (-f2.a) * f1.k; return Long.compare(left, right); }); long totalK = 0; long totalKC = 0; for (Function func : list) { totalK += func.k; totalKC -= func.a; } long[] prefixW = new long[m]; long[] prefixC = new long[m]; prefixW[0] = list.get(0).k; prefixC[0] = -list.get(0).a; for (int i = 1; i < m; i++) { prefixW[i] = prefixW[i - 1] + list.get(i).k; prefixC[i] = prefixC[i - 1] - list.get(i).a; } int low = l, high = r; long ans = Long.MAX_VALUE; while (high - low > 2) { int mid1 = low + (high - low) / 3; int mid2 = high - (high - low) / 3; long f1 = F(mid1, list, prefixW, prefixC, totalK, totalKC, S); long f2 = F(mid2, list, prefixW, prefixC, totalK, totalKC, S); if (f1 < f2) { high = mid2 - 1; } else if (f1 > f2) { low = mid1 + 1; } else { low = mid1 + 1; high = mid2 - 1; } } for (int x = low; x <= high; x++) { long fx = F(x, list, prefixW, prefixC, totalK, totalKC, S); if (fx < ans) ans = fx; } bw.write(ans + "\n"); } bw.flush(); } private static long F(int x, List<Function> list, long[] prefixW, long[] prefixC, long totalK, long totalKC, long S) { int low = 0, high = list.size() - 1; int pos = -1; while (low <= high) { int mid = (low + high) >>> 1; Function func = list.get(mid); if ((long) func.a >= -(long) x * func.k) { pos = mid; low = mid + 1; } else { high = mid - 1; } } long sum_left_k = (pos == -1) ? 0 : prefixW[pos]; long sum_left_kc = (pos == -1) ? 0 : prefixC[pos]; long F1 = (long) x * (2 * sum_left_k - totalK) + (totalKC - 2 * sum_left_kc); return F1 + S; } }
https://www.nowcoder.com/discuss/727521113110073344
思路:
- 常数处理:当 𝑘𝑖=0 时,函数为常数,直接加入总和 𝑆。
- 排序:对 𝑘𝑖>0 的函数按 𝑐𝑖=−𝑎𝑖/𝑘𝑖 排序,使用交叉相乘避免浮点数精度问题。
- 前缀和:计算权重和 𝑝𝑟𝑒𝑓𝑖𝑥𝑊 及常数和 𝑝𝑟𝑒𝑓𝑖𝑥𝐶 的前缀数组,用于快速计算 𝐹(𝑥)。
- 三分搜索:利用凸函数性质,在区间 [𝑙,𝑟] 上三分查找最小值点。
- 二分定位:对每个 𝑥,二分查找满足 𝑐𝑖≤𝑥 的最大位置,使用前缀和公式高效计算函数值。