gmoj 6829. 【2020.10.25提高组模拟】异或

(Solution)

异或这个东西似乎很有看头,尝试操作一波,但发现无论是建树还是从每一位上面入手似乎都不太可以。
于是最后打了个暴力走人。
正解有一个性质,就是在排序以后,只有相邻两个数异或可能成为最小值。
具体的就是,(a<b<c),则只有(a xor b)(b xor c)最小,一定有其中一个小于等于(a xor c)
所以我们只需要排一遍序,然后通过(trie)来统计答案即可。边询问边插入。

(Code)

#include <cstdio>
#include <algorithm>
#define N 300010
#define mo 998244353
#define db double
#define ll long long
#define mem(x, a) memset(x, a, sizeof x)
#define mpy(x, y) memcpy(x, y, sizeof y)
#define fo(x, a, b) for (int x = (a); x <= (b); x++)
#define fd(x, a, b) for (int x = (a); x >= (b); x--)
#define go(x) for (int p = tail[x], v; p; p = e[p].fr)
#define plus(x, y) x = x + y >= mo ? x + y - mo : x + y
using namespace std;
int f[N], t[N * 80], son[N * 80][2];
ll X, a[N], cf[62];
int n, ans = 0, tot = 1;

inline ll read() {
	ll x = 0; int f = 0; char c = getchar();
	while (c < '0' || c > '9') f = (c == '-') ? 1 : f, c = getchar();
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return f ? -x : x;
}

void insert(ll x, int pl) {
	int now_ = 1; t[1] += pl;
	fd(i, 60, 1) {
		int k = (x & cf[i]) != 0;
		if (! son[now_][k]) son[now_][k] = ++tot;
		now_ = son[now_][k], plus(t[now_], pl);
	}
}

int query(ll x) {
	int now_ = 1; ll s = 1;
	fd(i, 60, 1) {
		if (! now_ || ! t[now_]) return s;
		int k = (X & cf[i]) != 0, k1 = (x & cf[i]) != 0;
		if (k) now_ = son[now_][k1 ^ 1];
		else plus(s, t[son[now_][k1 ^ 1]]), now_ = son[now_][k1];
	}
	plus(s, t[now_]);
	return s;
}

int main()
{
	freopen("xor.in", "r", stdin);
	freopen("xor.out", "w", stdout);
	n = read(), X = read();
	cf[1] = 1; fo(i, 2, 60) cf[i] = cf[i - 1] << 1;
	fo(i, 1, n) a[i] = read();
	sort(a + 1, a + n + 1);
	ans = f[1] = 1, insert(a[1], f[1]);
	fo(i, 2, n) {
		f[i] = query(a[i]);
		plus(ans, f[i]), insert(a[i], f[i]);
	}
	printf("%d
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/jz929/p/13908727.html