[图论记录]CF1849F XOR Partition
题意:
思路:
idea from 洛谷题解
那篇题解好像是受到了某个 LGM 大佬的评论的启发。
我们要求的是某个最小值的最大值,这让我们联想到二分。
现在我们就要判断是否能有一种划分使得价值
那对于确定的
但是这样建图的时间复杂度是
首先,异或运算有一个性质。
性质: 若
证明:
考虑
两种情况都成立,故结论正确。
这个性质相当于告诉我们:一组数中两两异或的最小值,相当于把这组书排序后,相邻数之间异或最小值。
现在我们将
根据上面的性质,我们发现如果
如果
也就是说,
考试时如果能推到这就随便弄个
但是我们还是证明一下结论:
结论: 若
证明:
依然是考虑第一个不完全相同的位第
对于前两种情况,前三个数之间两两连边,构成了一个奇环;对于后两种情况,后三个数构成一个奇环。
有奇环说明不是二分图,故
对于
所以我们对于
如果最后是二分图,则肯定不存在
所以边数降至
这道题最关键的地方在于去掉没有用的边,简化边的数量。
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 2e5 + 5;
int n;
pair<int, int> a[MAXN];
vector<int> e[MAXN];
void add_edge(int u, int v) {
e[u].push_back(v);
e[v].push_back(u);
}
int clr[MAXN] = {0};
bool flg;
void dfs(int x, int c) {
if (clr[x] != -1) {
if (clr[x] != c)
flg = false;
return;
}
clr[x] = c;
for (auto i: e[x])
dfs(i, 1 - c);
}
bool chk(int x) {//能否分成两个集合使得总价值不小于 x
for (int i = 1; i <= n; i++)
e[i].clear(), clr[i] = -1;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= i + 3 && j <= n; j++)
if ((a[i].first ^ a[j].first) < x)
add_edge(a[i].second, a[j].second);
flg = true;
for (int i = 1; i <= n; i++)
if (clr[i] == -1)
dfs(i, 0);
return flg;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].first, a[i].second = i;
sort(a + 1, a + n + 1);
int l = 1, r = (1 << 30) + 1;//[l, r)
while (l + 1 < r) {
int mid = (1ll * l + r) / 2;
if (chk(mid))
l = mid;
else
r = mid;
}
for (int i = 1; i <= n; i++)
e[i].clear(), clr[i] = -1;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= i + 3 && j <= n; j++)
if ((a[i].first ^ a[j].first) < l)
add_edge(a[i].second, a[j].second);
flg = true;
for (int i = 1; i <= n; i++)
if (clr[i] == -1)
dfs(i, 0);
for (int i = 1; i <= n; i++)
cout << clr[i];
return 0;
}