Wannafly挑战赛14 E 无效位置(线性基+并查集)

链接:https://www.nowcoder.com/acm/contest/81/E
来源:牛客网

题目描述

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

输入描述:

 
  

第一行读入一个正整数(1 <= n <= 105)

第二行读入n个正整数,第i个表示a[i](0<= a[i] <= 109)

第三行读入n个正整数,第i个表示x[i]即第i次操作的位置,保证x[i]互不相同。

解题思路:线性基维护异或最大和,然后用并查集维护区间连通性,我们把所有查询从后往前考虑,就相当于不断地插入数字,然后就不断的合并区间(线性基)即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct XXJ
{
    ll d[61];
    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;
    }
    ll query_max()
    {
        ll ret = 0;
        for (int i = 60; i >= 0; i--)
            if ((ret ^ d[i]) > ret)
                ret ^= d[i];
        return ret;
    }
    ll query_min()
    {
        for (int i = 0; i <= 60; i++)
            if (d[i])
                return d[i];
        return 0;
    }
};

void merge(XXJ &x, XXJ &y, XXJ &z)
{
    XXJ tmp;
    for (int i = 60; i >= 0; i--)
        tmp.d[i] = y.d[i];
    for (int i = 60; i >= 0; i--)
        if (z.d[i])
        {
            ll tp = z.d[i];
            for (int i = 60; i >= 0; i--)
                if (tp & (1LL << i))
                    if (!tmp.d[i])
                    {
                        tmp.d[i] = tp;
                        break;
                    }
                    else
                        tp ^= tmp.d[i];
        }
    for (int i = 60; i >= 0; i--)
        x.d[i] = tmp.d[i];
}

int a[100005];
int q[100005];
XXJ s[100005];
int vis[100005];
int pre[100005];

int find(int x)
{
    return pre[x] == x ? x : pre[x] = find(pre[x]);
}

int main()
{

    for (int i = 0; i <= 100000; i++)
        pre[i] = i;

    int N;
    scanf("%d", &N);
    for (int i = 0; i < N; i++)
    {
        scanf("%d", &a[i]);
        s[i + 1].insert(a[i]);
    }

    for (int i = 0; i < N; i++)
    {
        scanf("%d", &q[i]);
    }

    ll ans = -1000;

    for (int i = N - 1; i >= 0; i--)
    {

        if (vis[q[i] - 1] == 1)//是否与左边联通,是的话就合并
        {
            int fx = find(q[i] - 1);
            int fy = find(q[i]);
            pre[fy] = fx;
            vis[q[i]] = 1;
            merge(s[fx], s[fy], s[fx]);
        }

        if (vis[q[i] + 1] == 1)//右边
        {
            int fx = find(q[i] + 1);
            int fy = find(q[i]);
            pre[fy] = fx;
            vis[q[i]] = 1;
            merge(s[fx], s[fy], s[fx]);
        }

        int fx = find(q[i]);
        vis[q[i]] = 1;
        ans = max(ans, s[fx].query_max());//ans同时记录了上次的最大值,这样就能顺便把其他区间的查询了
        q[i] = ans;
    }

    for (int i = 0; i < N; i++)
        printf("%d\n", q[i]);

    return 0;
}




猜你喜欢

转载自blog.csdn.net/lzc504603913/article/details/80024233