abcdeffa's Blog

当局者迷,旁观者清。

0%

GMOJ S5432 【三元组】

Description

有 $(X+Y+Z)$ 个三元组 $(x_i, y_i, z_i)$,请你从每个三元组中挑数,并满足每个三元组中可以且仅可以选择一个数,且选择 $x_i$ 的三元组个数恰好为 $X$、选择 $y_i$ 的三元组个数恰好为 $Y$、选择 $z_i$ 的三元组个数恰好为 $Z$。

问选出的数的和最大是多少。

Solution

考虑一下如果 $X = 0$ 该怎么做。

那么显然如果 $(y_i - z_i)$ 越大,将当前选的一个 $z_i$ 换为 $y_i$ 就更优。

然后取 $(y_i - z_i)$ 前 $Y$ 大的 $Y$ 个三元组的 $y_i$,然后其余的取 $z_i$ 即可。

现在考虑一下加入了 $X$ 以后怎么做。

显然如果 $(y_i - x_i)$ 越大,将当前选的一个 $x_i$ 换为 $y_i$ 就更优。

同理,如果 $(z_i - x_i)$ 越大,将当前选的一个 $x_i$ 换为 $z_i$ 就更优。

考虑先把全部的 $x_i$ 选了,然后再把选的 $Y$ 个 $x_i$ 换成 $y_i$,$Z$ 个 $x_i$ 换成 $z_i$ 即可。

然后考虑一下,如果要换的话,如果 $(z_i - y_i)$ 越大,那么换成 $z_i$ 比换成 $y_i$ 要优。

于是我们考虑先把这 $N = (X + Y + Z)$ 个三元组按照 $(z_i - y_i)$ 来排序。

由于有 $x_i$ 的干扰,我们不能够直接把前 $Z$ 个换成 $z_i$,把第 $(Z + 1)$ 到第 $(Z + Y)$ 个换成 $y_i$。

于是我们考虑枚举一个分界点,在分界点及之前的,我们可以把 $x_i$ 换成 $z_i$,在分界点之后的,我们可以把 $x_i$ 换成 $y_i$。

那如果要换的话,肯定是把 $(z_i - x_i)$ 前 $Z$ 大的 $Z$ 个三元组中选的 $x_i$ 给换成 $z_i$。

这个你可以用数据结构来维护,但是由于 $x_i, y_i, z_i$ 的值域都很小,我们可以用一个桶来维护当前的第 $Z$ 小值。

由于当分界点一直向后移的时候,存储 $(z_i - x_i)$ 的这个桶是不断加数的,比较好维护。

但 $(y_i - x_i)$ 的这个桶是不断减数的,不太好维护。

于是我们考虑把分界点一直往后挪,求出在任一分界点下,分界点及之前的这一部分的答案。

然后再把分界点一直往前挪,求出在任一分界点下,分界点之后的这一部分的答案,再与之前得到的分界点及之前的这一部分的答案相加,再取个最大值即可。

Code

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define add 500010
#define maxN 1000010
using namespace std;
struct Triplet{ long long x, y, z; } a[maxN];
long long bucket[maxN], cu[maxN];
long long min (long long x, long long y)
{
return x < y ? x : y;
}
bool cmp (Triplet A, Triplet B)
{
return A.z - A.y > B.z - B.y;
}
int main ()
{
long long Ans = 0, X = 0, Y = 0, Z = 0;
scanf("%lld %lld %lld", &X, &Y, &Z);
const long long N = X + Y + Z;
for(long long i = 1;i <= N; i++)
{
scanf("%lld %lld %lld", &a[i].x, &a[i].y, &a[i].z);
Ans += a[i].x;
}
sort(a + 1, a + N + 1, cmp);
long long nowMin = 10000000000000000LL, j = 0;
for(long long i = 1;i <= Z; i++)
{
long long x = a[i].x, z = a[i].z;
bucket[z - x + add]++;
nowMin = min(nowMin, z - x + add);
Ans += z - x;
}
cu[Z] = Ans;
const long long maxI = N - Y;
for(long long i = Z + 1;i <= maxI; i++)
{
long long x = a[i].x, z = a[i].z;
long long now = z - x + add;
if(now < nowMin)
{
cu[i] = Ans;
}
else
{
Ans -= nowMin, j++;
bucket[now]++;
if(j >= bucket[nowMin])
{
j = 0, nowMin++;
while(!bucket[nowMin])
{
nowMin++;
}
}
Ans += now;
cu[i] = Ans;
}
}
memset(bucket, 0, sizeof(bucket));
const long long minI = N - Y + 1;
nowMin = 10000000000000000LL, Ans = 0;
for(long long i = N;i >= minI; i--)
{
Ans += a[i].y - a[i].x;
long long x = a[i].x, y = a[i].y;
bucket[y - x + add]++;
nowMin = min(nowMin, y - x + add);
}
long long Res = Ans + cu[Z];
for(long long i = N - Y;i > Z; i--)
{
long long x = a[i].x, y = a[i].y;
long long now = y - x + add;
if(now < nowMin)
{
Res = max(Res, Ans + cu[i - 1]);
}
else
{
Ans -= nowMin, j++;
bucket[now]++;
if(j >= bucket[nowMin])
{
j = 0, nowMin++;
while(!bucket[nowMin])
{
nowMin++;
}
}
Ans += now;
Res = max(Res, Ans + cu[i - 1]);
}
}
printf("%lld", Res);
return 0;
}