2021杭电多校赛第一场
2021“MINIEYE杯”中国大学生算法设计超级联赛(1)
Xor sum
因为异或和加有着相似的性质,我们可以利用前缀和的思想快速求出区间和,也可以利用前缀异或思想快速求出区间异或,那么本题就变成了在前缀异或数组\(xor\)中选出两个正整数\(l\),\(r\)使得\([l,r]\)异或和大于等于\(k\),而这利用字典树可以很好的解决,我们可以在插入一个数\(xor_i\)的同属记录出现\(xor_i\)这个元素的最大下标\(pos\),然后我们枚举右端点,在字典树中贪心地查找\(x\),使得\(x \bigotimes xor_i\)大于等于\(k\)。因为指针版超时了,所以这里用了数组版字典树。
#include <bits/stdc++.h>
using namespace std;
class Trie {
private:
static const int MAXN = 1e5 + 5;
static const int BASE = 30;
int idx = 1, trie[MAXN * BASE][2];
int pos[MAXN * BASE]; // pos[i]: 异或前缀为i的最右端点
public:
inline void insert(int p, int x) {
int u = 1;
for (int i = BASE; i >= 0; --i) {
int v = (x >> i) & 1;
if (!trie[u][v]) {
trie[u][v] = ++idx;
}
u = trie[u][v];
pos[u] = max(pos[u], p);
}
}
inline int find(int x, int k) {
int u = 1, res = -1;
for (int i = BASE; i >= 0; --i) {
int v = (x >> i) & 1;
if ((k >> i) & 1) { // 如果k当前位为1
// 只有当前位x能找和它异或为1的数才有可能大于等于k
// 否则直接返回已经找到的最大值
if (trie[u][v ^ 1]) {
u = trie[u][v ^ 1];
}
else {
return res;
}
}
else { // 如果k当前位为0, x异或后当前位是0是1都有可能大于等于k
// 如果异或后是1, 那肯定大于等于k了, 不用往下再遍历了, 直接尝试更新最大值
// 否则继续往下遍历
if (trie[u][v ^ 1]) {
res = max(res, pos[trie[u][v ^ 1]]);
}
u = trie[u][v];
}
}
res = max(res, pos[u]); // 到达最底层即==k的情况
return res;
}
inline void clear() {
for (int i = 1; i <= idx; ++i) {
pos[i] = 0;
trie[i][0] = trie[i][1] = 0;
}
idx = 1;
}
} tree;
const int MAXN = 1e5 + 5;
int a[MAXN];
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
tree.clear();
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
a[i] ^= a[i - 1];
}
int L = -1, R = n + 1;
for (int r = 1; r <= n; ++r) {
tree.insert(r, a[r]);
int l = tree.find(a[r], k);
if (~l && r - l < R - L) {
L = l, R = r;
}
}
if (L == -1) cout << -1 << '\n';
else cout << L + 1 << ' ' << R << '\n';
}
system("pause");
return 0;
}
zoto
题目所给\(x\),\(y\)达到了\(1e5\),所以不能用二维数据结构,考虑如何将二维转换成一维的问题,因为题目保证对于每个\(i\)只有一个\(f_i\)和他对应,那么对于每一行其实只有一个点。然后我们可以用莫队对每行的点进行维护,同时用树状数组维护\(y\)轴。每次\(add\)或者\(del\)操作时,记录不同\(y\)坐标的个数的同时,在树状数组上相应的位置进行增减,对于每个询问\((x1,y1)\),\((x2,y2)\)我们只要用树状数组询问\(y2\),\(y1\)然后相减即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int belong[MAXN];
struct Query {
int idx, x1, y1, x2, y2;
bool operator <(const Query& p) const {
if (belong[x1] ^ belong[p.x1]) {
return belong[x1] < belong[p.x1];
}
return belong[x1] & 1 ? x2 < p.x2 : x2 > p.x2;
}
} q[MAXN];
int n, m, a[MAXN], ans[MAXN];
int lowbit(int x) {
return x & -x;
}
int tree[MAXN];
void add(int i, int k) {
for ( ; i <= n; i += lowbit(i)) {
tree[i] += k;
}
}
int query(int i) {
int res = 0;
for ( ; i; i -= lowbit(i)) {
res += tree[i];
}
return res;
}
int cnt[MAXN];
void del(int p) {
int x = a[p];
if (!--cnt[x]) add(x, -1);
}
void add(int p) {
int x = a[p];
if (!cnt[x]++) add(x, 1);
}
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
memset(cnt, 0, sizeof(cnt));
memset(tree, 0, sizeof(tree));
cin >> n >> m;
int t = sqrt(n);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
++a[i];
belong[i] = (i - 1) / t + 1;
}
for (int i = 1; i <= m; ++i) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
q[i] = {i, x1, ++y1, x2, ++y2};
}
sort(q + 1, q + m + 1);
for (int i = 1, l = 1, r = 0; i <= m; ++i) {
int ql = q[i].x1, qr = q[i].x2;
while (l < ql) del(l++);
while (l > ql) add(--l);
while (r < qr) add(++r);
while (r > qr) del(r--);
ans[q[i].idx] = query(q[i].y2) - query(q[i].y1 - 1);
}
for (int i = 1; i <= m; ++i) {
cout << ans[i] << '\n';
}
}
system("pause");
return 0;
}

浙公网安备 33010602011771号