Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

CF802O April Fools' Problem (hard)

LuoGu: CF802O April Fools’ Problem (hard)

CF: O. April Fools’ Problem (hard)

答案是关于 $k$ 的凸函数,随着 $k$ 增大,答案一定增大,增大的速度越来越快。因此可以用 wqs二分

现在考虑每个匹配都减少一个权值,没有了 $k$ 的限制。可以用二维 DP 完成,时间复杂度为 $O(n ^ 2 \log n)$ ,不能通过此题。

考虑带悔贪心。每次将 $a _ i$ 放入堆中,每个后面的 $b _ i$ 可以匹配到前面的 $a _ i$ 。考虑反悔,如果 $b _ j < b _ i$ , $b _ j$ 可以替换 $b _ i$ 。因此,每次匹配后,将 $b _ i$ 放入堆中。

细节:

  • 本题中,斜率不是严格单调的,因此一个斜率意味着多个答案。在贪心中,相等的答案尽量不取,答案就可以所有该斜率中的最小值。找到一个 $mid$ 使得 $cnt \le m$ ,而 $mid + 1$ 则 $cnt > m$ ,那么 $k$ 在斜率 $mid$ 上,可以确定答案。
  • 注意一个匹配,减少一个权值,二分的范围为一个匹配的值域。
查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <cstdio>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PLI;
const int N = 5e5 + 10;
LL ans;
int n, m, cnt, A[N], B[N];
void check(LL x)
{
cnt = ans = 0;
priority_queue<PLI, vector<PLI>, greater<PLI>> q;
for (int i = 1; i <= n; i++)
{
q.push(make_pair(A[i] - x, 1));
PLI t = q.top();
if (B[i] + t.first >= 0)
continue;
q.pop();
cnt += t.second;
ans += B[i] + t.first;
q.push(make_pair(-B[i], 0));
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &A[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &B[i]);
LL l = 0, r = 2e9, res;
while (l <= r)
{
LL mid = l + r >> 1;
check(mid);
cnt <= m ? (res = ans + (LL)mid * m, l = mid + 1) : r = mid - 1;
}
printf("%lld", res);
return 0;
}

用模拟费用流也可以理解此题,见 P4694 。