intmain(){ std::ios::sync_with_stdio(false); std::cin.tie(0); int m, p; cin >> m >> p; int tmp; for (int i = 1; i <= m; i++) { cin >> tmp >> a[i]; a[i] -= tmp; } sort(a + 1, a + m + 1); // 前缀和 for (int i = 1; i <= m; i++) { sum[i] = sum[i - 1] + a[i]; } for (int j = 1; j <= m; j++) { dp[1][j] = a[j] * j - sum[j]; } for (int i = 2; i <= p; i++) { // 斜率优化 int head = 0, tail = 0; // 必然吧 dp[i][1] = 0; ndeq[tail] = 0; tail++; for (int j = 2; j <= m; j++) { // 准备插入j,维护斜率递增,左侧斜率小于右侧斜率 while (tail - head >= 2 && grad(dp[i - 1], ndeq[tail - 2], ndeq[tail - 1]) >= grad(dp[i - 1], ndeq[tail - 1], j)) { // 出栈 tail--; } // 这一点应该是一定会加入的 ndeq[tail] = j; tail++; // 找不到情况队处理 dp[i][j] = dp[i - 1][j]; // TODO: 二分查找 // 大概直接查找就完事了 for (int k = head; k < tail - 1; k++) { // 斜率递增,故左斜率<=a[j]&&右斜率>=a[j]时最优 if (dp[i - 1][ndeq[k + 1]] + sum[ndeq[k + 1]] - dp[i - 1][ndeq[k]] - sum[ndeq[k]] >= a[j] * (ndeq[k + 1] - ndeq[k]) && dp[i - 1][ndeq[k]] + a[j] * (j - ndeq[k]) - (sum[j] - sum[ndeq[k]]) <= dp[i][j]) { // 是这点了 dp[i][j] = dp[i - 1][ndeq[k]] + a[j] * (j - ndeq[k]) - (sum[j] - sum[ndeq[k]]); // 修改队首 head = k; break; } } head = min(head, tail - 1); } } cout << dp[p][m] << endl; return0; }
测试例生成(Python)
为了方便直接将位置都设为0了。
1 2 3 4 5 6 7 8 9 10 11
defe(m, p, max=1e9, filename='in'): m = int(m) p = int(p) fi = open(filename, 'w') fi.write("{} {}\r\n{}\r\n".format( m, p, "\r\n".join([ "0 {}".format(random.randint(0, max)) for i inrange(m) ]) )) fi.close()