HDU6579 Operation(线性基)

题目链接:Operation

题意:

给出一个长度为n的序列a[1...n],然后是m个一下操作之一:(1\leq n,m\leq 5*10^5,0\leq a[i]< 2^{30})

  • 0 l r: select some numbers from al...ar so that their xor sum is maximum, and print the maximum value.
  • 选择任意个[l, r]区间内的数,输出它们的异或和最大值
  • 1 x: append x to the end of the sequence and let n=n+1.
  • 将x加入到序列a的末尾,并将n=n+1

And operations will be encrypted. You need to decode the operations as follows, where lastans denotes the answer to the last type 0 operation and is initially zero: 
For every type 0 operation, let l=(l xor lastans)mod n + 1, r=(r xor lastans)mod n + 1, and then swap(l, r) if l>r.
For every type 1 operation, let x=x xor lastans.

强制在线回答询问。

 

题目分析:

一开始我们想到的做法是利用线性基可合并性质,在线段树上维护线性基,但是做法复杂度是O(NlogN*30*30)的,场上直接T飞。

上述做法不能AC这道题,可行做法如下:

记录数组r[N][30]和数组p[N][30],其中r[i]表示数组中a[i...N]构成的线性基,同时p[i]记录对应基在序列中的最小下标

对于询问[ l, r]的处理:

线性基r[l]中对应基在序列中下标小于等于r的元素,就是对应数组a[l...r]元素构成的线性基,直接就可以获取到最大异或值了。

得到线性基数组的过程如下(1类型操作也是同样做法):
        for(int i = 1; i <= n; i++) {
            for(int j = i; j >= 1; j --)
                if(!add(r[j], a[i], p[j], i))
                    break;
        }

其中add函数传入线性基指针r[i]和元素a[i],以及p[i]和当前元素位置i(用于记录对应基的序列下标),表示将元素插入线性基中,插入成功返回true,失败返回false。

以上代码看似是O(N*N*30),但实际上可以证明复杂度不会超过O(N*30),因为一个基上只有30个空位balabala,自己理解一下。

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;

inline bool add(int r[], int x, int p[], int y) {
    for(int i = 29; x; --i)
        if(x & (1 << i))
            if(r[i])
                x ^= r[i];
            else {
                r[i] = x, p[i] = y;
                return true;
            }
    return false;
}

int a[MAXN], r[MAXN][30], p[MAXN][30];
int T, n, m, op, L, R, ans;

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) {
            scanf("%d", a + i);
            for(int j = i; j >= 1; j --)
                if(!add(r[j], a[i], p[j], i))
                    break;
        }
        ans = 0;
        while(m--) {
            scanf("%d", &op);
            if(op == 0) {
                scanf("%d%d", &L, &R);
                L = (L ^ ans) % n + 1;
                R = (R ^ ans) % n + 1;
                if(L > R)
                    swap(L, R);
                ans = 0;
                for(int i = 29; i >= 0; i--)
                    if(r[L][i] && p[L][i] <= R && ans < (ans ^ r[L][i]))
                        ans ^= r[L][i];
                printf("%d\n", ans);
            } else {
                scanf("%d", &L);
                L ^= ans;
                n = n + 1;
                for(int j = n; j >= 1; j--)
                    if(!add(r[j], L, p[j], n))
                        break;
            }
        }
        if(T)   //多组样例初始化
            memset(r, 0, (sizeof r[0]) * (n + 1));
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值