BZOJ 2741 【FOTILE模拟赛】L 可持久化Trie 分块

题意

在线求a[l,r]间最大连续子串xor和。

题解

我的程序竟然排Rank 4,不科学。。。
a[l,r] 的xor和转化为 b[l1] xor b[r]
那么问题转化为求 b[l1,r] 的任意2个数的xor和的最大值。
如果一个数固定,那么就可以把问题转化为BZOJ 3261。
所以我们每 n 取一个数,预处理出它到后面每个数的xor的最大值,比如对于 a[n] a[j] ,我们先假定 a[j] 前的范围的最大xor值已求得,那么我们固定 a[j] ,那么这段数中找与a[j]的xor和的最大值为 O(logm) 。而对于 a[j] 之前的数,我们之前已经先求得了,递推一下就好了。似乎是个dp?
预处理的复杂度为 O(nnlogm) ,每 n 取一次数,枚举该数后面的数,查询Trie。
那么对于每个询问就好办了。
假如我们查询 [l,r] ,那么先判定区间内有无之前每 n 取的数,那么该数到r这段区间的最大xor值已经预处理出来了。对于l到该数间的数我们暴力查询Trie就可以了,因为数目不会超过 O(n)
每个询问的复杂度为 O(nlogm)

可以尝试把块的大小改一下,测试发现分块开109比110要快一些。。

代码

#include <cstdio>
#include <algorithm>
#define FOR(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int N = 12002;
struct Trie {
    Trie* c[2]; int size;
    Trie() { c[0] = c[1] = this; size = 0; }
    Trie(Trie *l, Trie *r, int sz) { c[0] = l, c[1] = r, size = sz; }
    void* operator new(size_t) {
        static Trie pool[N * 35], *pointer = pool;
        return pointer++;
    }
    static Trie* put(Trie *p, int x, int digit) {
        if (!digit)
            return new Trie(p->c[0], p->c[1], p->size + 1);
        if (x & digit)
            return new Trie(p->c[0], put(p->c[1], x, digit >> 1), p->size + 1);
        else
            return new Trie(put(p->c[0], x, digit >> 1), p->c[1], p->size + 1);
    }
    static int get(Trie *p1, Trie *p2, int x, int digit) {
        if (!digit)
            return 0;
        int t = bool(~x & digit);
        if (p1->c[t]->size - p2->c[t]->size)
            return digit | get(p1->c[t], p2->c[t], x, digit >> 1);
        else
            return get(p1->c[t ^ 1], p2->c[t ^ 1], x, digit >> 1);
    }
} *trie_impl[N], **trie = trie_impl + 1;
const int block = 109;
const int dep = 1 << 30;
int w[115][N];
int b[N], n, m;
int main() {
    int lastans = 0, l, r, x, y, i, j;
    scanf("%d%d", &n, &m);
    FOR(i,1,n) scanf("%d", &b[i]), b[i] ^= b[i - 1];
    trie[-1] = new Trie();
    FOR(i,0,n) trie[i] = Trie::put(trie[i - 1], b[i], dep);
    FOR(i,1,n) FOR(j, 0, i / block)
        w[j][i] = max(w[j][i - 1], Trie::get(trie[j * block - 1], trie[i], b[i], dep));
    FOR(i,1,m) {
        scanf("%d%d", &x, &y);
        l = min(((x + lastans) % n) + 1, ((y + lastans) % n) + 1) - 1;
        r = max(((x + lastans) % n) + 1, ((y + lastans) % n) + 1);
        int ans = 0, l_id = l / block, r_id = r / block, j;
           ans = w[l_id + 1][r];
        FOR(j, l, min(r, (l_id + 1) * block))
           ans = max(ans, Trie::get(trie[l - 1], trie[r], b[j], dep));
        printf("%d\n", ans);
        lastans = ans % n;
    }
    return 0;
}

Markdown的编辑器似乎并没有办法把网页格式保留下来?

原题

2741: 【FOTILE模拟赛】L

Time Limit: 15 Sec Memory Limit: 162 MB
Submit: 1817 Solved: 493

Description

FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和。
即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 … xor Aj),其中l<=i<=j<=r。
为了体现在线操作,对于一个询问(x,y):
l = min ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).
r = max ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).
其中lastans是上次询问的答案,一开始为0。

Input

第一行两个整数N和M。
第二行有N个正整数,其中第i个数为Ai,有多余空格。
后M行每行两个数x,y表示一对询问。

Output

共M行,第i行一个正整数表示第i个询问的结果。

Sample Input

3 3
1 4 3
0 1
0 1
4 3

Sample Output

5
7
7

HINT

N=12000,M=6000,x,y,Ai在signed longint范围内。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值