洛谷P5283 [十二省联考 2019] 异或粽子 题解
题意:
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有 n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 1 到 n。第 i 种馅儿具有一个非负整数的属性值 ai。每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出 k 个粽子。
小粽的做法是:选两个整数数 l, r,满足 1≤l≤r≤n,将编号在 [l,r] 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。(异或就是我们常说的 xor 运算,即 C/C++ 中的
ˆ
运算符或 Pascal 中的xor
运算符)小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的
粽子。小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!
输入格式:
第一行两个正整数 n, k,表示馅儿的数量,以及小粽打算做出的粽子的数量。
接下来一行为 n 个非负整数,第 i 个数为 ai,表示第 i 个粽子的属性值。
输出格式:
输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。
数据范围:
1≤n≤5×105, 1≤k≤min{n(n−1)2,2×105}, 0≤ai≤232−1 。
明天端午节,写个和粽子有关的题。好吧其实这道题和粽子没什么关系。
就像这道题和什么可持久化 Trie 没关系一样(就算有关我也不会)
按照套路,我们对原数组做一遍前缀异或和,那么就是找到异或值最大的 k 对 i,j 满足 0≤i<j≤n 。
最令人头疼的就是这个 i,j 需要满足 i<j ,否则我们可以直接用下面的算法求出答案:
- 将 0 到 n 的所有前缀异或和的最大匹配塞入优先队列。(最大匹配指使得他们异或值最大的另一个位置)
- 每次取出队首元素,然后将第二大的匹配塞入优先队列,这个过程类似于平衡树的操作。
- 重复以上过程,直到不能再取。由于 i,i 的答案一定为 0 ,所以这个算法只会多算 i>j 的情况。
- 注意步骤二中不一定是第二大匹配(只是为了方便描述),设队首是第 x 大,那就是 x+1 大的匹配。
然后我们发现,不限制 i,j 的话,最优方案一定会尽可能把 i,j 和 j,i 都取到。
那么我们完全可以直接取 2k 个,然后把答案除以 2 。
时间复杂度 O(nlogV+nlogn)
代码:
#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
typedef long long ll;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define N ((int)(5e5 + 15))
struct node { int id, rk; ll v; };
bool operator<(node a, node b) { return a.v < b.v; }
priority_queue<node> q; ll res = 0, a[N];
int tot, sz[N * 20], tr[N * 20][2];
void insert(ll x)
{
int u = 0;
for(int i = 35; ~i; i--)
{
int c = (x >> i) & 1; ++sz[u];
if(!tr[u][c]) { tr[u][c] = ++tot; } u = tr[u][c];
}
++sz[u];
}
ll query(ll x, int rk)
{
int u = 0; ll sum = 0;
for(int i = 35; ~i; i--)
{
int c = (x >> i) & 1;
if(!tr[u][c ^ 1]) u = tr[u][c];
else {
if(rk <= sz[tr[u][c ^ 1]]) {
u = tr[u][c ^ 1], sum |= 1ll << i;
} else { rk -= sz[tr[u][c ^ 1]], u = tr[u][c]; }
}
}
return sum;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int n, m; cin >> n >> m; insert(0);
for(int i = 1; i <= n; i++) { cin >> a[i], a[i] ^= a[i - 1]; insert(a[i]); }
for(int i = 0; i <= n; i++) q.push({i, 1, query(a[i], 1)});
for(int i = 1; i <= 2 * m; i++)
{
auto t = q.top(); q.pop(); res += t.v;
if(t.rk < n) q.push({t.id, t.rk + 1, query(a[t.id], t.rk + 1)});
}
cout << res / 2 << '\n';
return 0;
}
参考文献: