2019多校第一场:Operation(前缀线性基)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6579

题意:现在有 n n n个数, q q q次操作,每次操作可以在 n n n个数末尾添加一个数,或者询问 l l l r r r区间任选数字出来异或和最大。并且每次操作和上一次的答案相关。

解题心得:

  • 一看区间询问异或和最大肯定时线性基没跑了。
  • 这个题和 C F 1100 F CF1100F CF1100F题有点像,但是强制在线了,如果不强制在线,那么就是一个贪心处理的线性基,具体解法见 https://blog.csdn.net/yopilipala/article/details/96997808。
  • 强制在线怎么办呢,那就求一个前缀线性基就行了,也就是说,因为线性基的空间占用很小就可以在每一个数字建立一个新的线性基,这个线性基和前面一个数字的线性基唯一的不同就是贪心插入了当前数字。这个时候就很简单了,和 C F 1100 F CF1100F CF1100F相同询问。


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e6+100;
const ll maxm = 35;
ll n, m, num[maxn], lastens;

struct Cxx {
    ll va, pos;
}cxx[maxn][maxm];//这里不但要记录值还需要记录一下位置,用于贪心

void init() {
    scanf("%lld%lld", &n, &m);
    lastens = 0;
    for(ll i=1;i<=n;i++) scanf("%lld", &num[i]);
}

void insert(ll va, ll pos) {
    for(ll i=0;i<maxm;i++) {
        cxx[pos][i].va = cxx[pos-1][i].va;
        cxx[pos][i].pos = cxx[pos-1][i].pos;
    }
    int Pos = pos;

    for(ll i=31;i>=0;i--) {//这里贪心插入
        if(va&(1<<i)) {
            if(cxx[Pos][i].va == 0) {
                cxx[Pos][i].va = va;
                cxx[Pos][i].pos = pos;
                break;
            } else if(cxx[Pos][i].pos < pos) {
                Cxx temp = cxx[Pos][i];
                cxx[Pos][i].va = va;
                cxx[Pos][i].pos = pos;
                va = temp.va;
                pos = temp.pos;
            }
            va ^= cxx[Pos][i].va;
        }
    }
}

ll query(ll l, ll r) {
    ll sum = 0;

    for(ll i=31;i>=0;i--) {
        if(cxx[r][i].pos >= l) {
            sum = max(sum ,sum^cxx[r][i].va);
        }
    }
    return sum;
}

int main() {
//    freopen("1.in.txt", "r", stdin);
    ll t; scanf("%lld", &t);
    while(t--) {
        init();
        for(ll i=1;i<=n;i++) insert(num[i], i); //将初始的n个数插进去
        while(m--) {
            ll ope; scanf("%lld", &ope);
            if(ope == 0) {
                ll l, r; scanf("%lld%lld",&l, &r);
                l = (l^lastens)%n+1;
                r = (r^lastens)%n+1;

                if(l > r) swap(l, r);

                lastens = query(l, r);
                printf("%lld\n", lastens);
            }else {
                ll x; scanf("%lld", &x);
                x = x ^ lastens;
                num[++n] = x;
                insert(num[n], n);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值