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;
}
posted @ 2021-07-30 13:02  stler  阅读(101)  评论(0)    收藏  举报