LightOJ 1269 - Consecutive Sum(字典树)

题目链接:LightOJ 1269 - Consecutive Sum

题目大意:给定一个序列,选定一段区间的亦或和,输出最大和最小。

解题思路:最大很简单,对所有前缀建立字典树,然后尽量往反向走;最小则需要往正向走,并且向正向走的时候要扣

除自己本身。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 50005 * 32;
const int sigma_size = 2;

struct Tire {
    int sz;
    int g[maxn][sigma_size];
    int c[maxn];

    void init();
    void insert(int s);
    int findMax(int s);
    int findMin(int s);
}T;

int N, A[maxn];

int main () {
    int cas, x;
    scanf("%d", &cas);
    for (int kcas = 1; kcas <= cas; kcas++) {
        T.init();
        scanf("%d", &N);
        for (int i = 1; i <= N; i++) {
            scanf("%d", &x);
            A[i] = A[i-1] ^ x;
        }

        for (int i = 0; i <= N; i++)
            T.insert(A[i]);

        int ansMax = 0, ansMin = (1<<31)-1;
        for (int i = 0; i <= N; i++) {
            ansMax = max(ansMax, T.findMax(A[i]));
            ansMin = min(ansMin, T.findMin(A[i]));
        }
        printf("Case %d: %d %d\n", kcas, ansMax, ansMin);
    }
    return 0;
}

void Tire::init() {
    sz = 1;
    c[0] = 0;
    memset(g[0], 0, sizeof(g[0]));
}

int Tire::findMin(int s) {
    int ret = 0, u = 0;
    for (int i = 30; i >= 0; i--) {
        int v = (s>>i) & 1;

        if (g[u][v] == 0 || (g[u][v^1] && c[g[u][v]] < 2)) {
            v = v ^ 1;
            ret |= (1<<i);
        } 

        u = g[u][v];
    }
    return ret;
}

int Tire::findMax(int s) {
    int ret = 0, u = 0;

    for (int i = 30; i >= 0; i--) {
        int v = ((s>>i)&1)^1;

        if (g[u][v])
            ret |= (1<<i);
        else
            v = v ^ 1;
        u = g[u][v];
    }
    return ret;
}

void Tire::insert(int s) {
    int u = 0;

    for (int i = 30; i >= 0; i--) {
        int v = (s>>i) & 1;

        if (g[u][v] == 0) {
            c[sz] = 0;
            memset(g[sz], 0, sizeof(g[sz]));
            g[u][v] = sz++;
        }

        u = g[u][v];
        c[u]++;
    }
}

你可能感兴趣的:(LightOJ 1269 - Consecutive Sum(字典树))