牛客挑战赛 14 E 题 无效位置 【线性基 + 并查集 + 逆向思维】 好题!

传送门
题目大意:

给一个1-base数组{a},有N次操作,每次操作会使一个位置无效。一个区间的权值定义为这个区间里选出一些数的异或和的最大值。求在每次操作前,所有不包含无效位置的区间的权值的最大值

思路: 首先我们应该想n个数中选择一些数异或起来最大怎么做? 这个用线性基可以做到.(不懂的自行百度), 在就是我们对于一个无效位置时很难控制我们在选的时候不选它, 所以我们逆向思考. 每次让一个位置无效相当于我们在可以选择的数中加了一个新的数, 也就是我们不断的合并两个线性基, 这样做就没有无效位置这个限制了… 然后连接的时候用并查集维护下. 其实这也是并查集中的逆向思考, 将集合的断开逆向变成合并两个集合……

AC Code

const int maxn = 1e5+5;
struct L_B {
    ll d[65], p[65];
    int cnt;
    void init() {
        Fill(d, 0); Fill(p, 0);
        cnt = 0;
    }  // 1e18以内的数都适用.
    bool Insert(ll val) {
        for (int i = 60 ; i >= 0 ; i --) {
            if (val & (1ll << i)) {
                if (!d[i]) {
                    d[i]=val;
                    break;
                }
                val^=d[i];
            }
        }
        return val > 0;
        // 可判断val是否存在于线性基当中.
    }
    ll query_max() {
        ll res = 0;
        for (int i = 60 ; i >= 0 ; i --) {
            if ((res^d[i]) > res) res ^= d[i];
        }
        return res;
    }
    ll query_min() {
        for (int i = 0 ; i <= 60 ; i ++) {
            if (d[i]) return d[i];
        }
        return 0;
    }
    void rebuild() { // 用于求第k小值.需要先进行独立预处理
        for (int i = 60 ; i >= 0 ; i --) {
            for (int j = i-1 ; j >= 0 ; j --) {
                if (d[i] & (1ll<<j)) d[i] ^= d[j];
            }
        }
        for (int i = 0 ; i <= 60 ; i ++) {
            if (d[i]) p[cnt++]=d[i];
        }
    }
    ll kthquery(ll k) {
        ll res = 0;
        if (k >= (1ll << cnt)) return -1;
        for (int i = 60 ; i >= 0 ; i --) {
            if (k & (1LL<<i)) res ^= p[i];
        }
        return res;
    }
    void Merge(const L_B &b) {
        for (int i = 60 ; i >= 0 ; i --)
            if (b.d[i]) Insert(b.d[i]);
    }
}e[maxn];
int a[maxn], q[maxn], ans[maxn];
int n;
int fa[maxn], r[maxn];
int vis[maxn];
void init() {
    for (int i = 1 ; i <= n ; i ++) {
        fa[i] = i;
    }
}
int Find(int x) {
    return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
void Un(int x, int y) {
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy) return ;
    if (fx < fy) swap(fx, fy);
    fa[fx] = fy;
}
void solve()
{
    scanf("%d", &n);
    for (int i = 1 ; i <= n ; i ++) {
        scanf("%d", &a[i]);
    }
    for (int i = 1 ; i <= n ; i ++ ) {
        scanf("%d", &q[i]);
    }
    ll res = 0; init();
    for (int i = n ; i >= 1 ; i --) {
        e[q[i]].Insert(a[q[i]]);
        vis[q[i]] = 1;
        if (vis[q[i]+1]) {
            int f1 = Find(q[i]+1);
            Un(q[i], f1);
            e[q[i]].Merge(e[f1]);
        }
        if (vis[q[i]-1]) {
            int f2 = Find(q[i]-1);
            Un(f2, q[i]);
            e[f2].Merge(e[q[i]]);
        }
        res = max(res, e[Find(q[i])].query_max());
        ans[i] = res;
    }
    for (int i = 1 ; i <= n ; i ++) {
        printf("%d\n", ans[i]);
    }
}

猜你喜欢

转载自blog.csdn.net/Anxdada/article/details/81316525