1. 1. 题目
  2. 2. 题解
  3. 3. 代码

题目

给定若干个自然数 a1na_{1\sim n}

你需要选出其中一些数,然后将你选出的数划分为若干个集合。

你需要最大化每个集合 mex\tt mex 的异或和,输出这个值。

一个集合的 mex{\tt mex} 是指最小的不在这个集合中的自然数,例如 {0,1,2,4,5}\{0,1,2,4,5\}mex{\tt mex}33{1,2,3}\{1,2,3\}mex{\tt mex}00

保证 1n1061\le n\le 10^60ain0\le a_i\le n

题解

首先可以判断出,如果一个集合的mex\text{mex}xx的话,那么这个集合一定要包含11x1x-1,而对于x后面的数要不要都无所谓

所以我们可以先取出最大的mex\text{mex},即最长的连续数+1

然后,考虑剩下的数。如果yy能凑出来那么对于z<yz < yzz一定能够凑出,所以想让z xor ansz \text{ }xor\text{ } ans最大,从大向小考虑,如果z&ans=0z \& ans=0,那么就选出11z1z-1

考场上就没想出来,-30pointsQAQ

代码

实现的不好,应该是O(n2)O(n^2)的,实际可以做到O(n)O(n)(但由于数据太水n方能过去)

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
#include<bits/stdc++.h>

using namespace std;

int read()
{
int f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}

while(ch >= '0' && ch <= '9')
{
x = 10 * x + ch - '0';
ch = getchar();
}
return f * x;
}

const int N = 1e6 + 10;
int a[N], n;
int cnt[N];
int maxa = -1;

long long ans;
int main()
{
freopen("mexor.in", "r", stdin);
freopen("mexor.out", "w", stdout);
n = read();

for(int i = 1;i <= n;i++)
{
a[i] = read();
cnt[a[i]]++;
maxa = max(maxa, a[i]);
}
while(1)
{
int i = -1;
for(;i <= maxa;i++)
{
if(cnt[i + 1] == 0)
{
break;
}
}
if(i == -1) break;
if((ans & (i + 1)) == 0)
{
ans += i + 1;
for(int j = 0;j <= maxa;j++)
{
cnt[j] --;
}
}
else
{
cnt[i] = 0;
}
}

printf("%lld", ans);

return 0;
}