码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ2741 【FOTILE模拟赛】L 【可持续化trie + 分块】

时间:2018-04-03 23:58:45      阅读:342      评论:0      收藏:0      [点我收藏+]

标签:lag   print   algo   开头   c++   inline   位置   正整数   开始   

题目

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。

输入格式

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

输出格式

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

输入样例

3 3

1 4 3

0 1

0 1

4 3

输出样例

5

7

7

提示

HINT

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

题解

区间异或和最大,转化为两个前缀和

多次询问不同区间,用可持久化trie树

但每次要任意选出两个数,而常规的trie树只支持一个数询问区间和它的最大异或值,不能处理区间内任意两个数异或和最大值
何破?

我们不可能每次询问\(O(n^2logn)\)枚举其中一个数
那就预处理!
如果我们能预处理出每个区间异或最大值,就是\(O(n^2logn)\)预处理,\(O(1)\)查询

能不能均摊一下?
分块!
我们只预处理每个块头到其后面所有位置的数异或的最大值
具体的,设\(f[i][j]\)表示\(i\)块开头位置到\(j\)中所有数异或的最大值,记块头为\(u\),则\(f[i][j]\)即为区间\([u,j]\)的答案
算出\(f[i][j]\)只需要枚举每个\(j\)就可以了
具体地,\(f[i][j] = max(f[i][j - 1],query(j,区间[u,j - 1]))\)

那么每次询问的时候,对于\(l\)之后的第一个块头\(u\),可以得到出后面的答案\(f[u][r]\)
所以我们只需要计算区间\([l,u - 1]\)的数与其后面的数的最大异或值
这个区间大小不会超过\(\sqrt{n}\),所以可以直接统计

总的复杂度\(O(n\sqrt{n}logn)\)
【坑点,给出的x,y可能超过int范围】

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define LL long long int
using namespace std;
const int maxn = 12005,Bit = 31,maxm = 6000000,INF = 100000000;
inline LL read(){
    LL out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
LL n,m,A[maxn],sum[maxn],bin[40];
LL f[200][maxn],block[maxn],B,lans;
struct trie{
    int ch[maxm][2],sum[maxm],rt[maxn],cnt;
    int ins(int pre,int x){
        int tmp,u;
        tmp = u = ++cnt;
        for (int i = Bit; i >= 0; i--){
            ch[u][0] = ch[pre][0];
            ch[u][1] = ch[pre][1];
            sum[u] = sum[pre] + 1;
            LL t = x & bin[i]; t >>= i;
            pre = ch[pre][t];
            u = ch[u][t] = ++cnt;
        }
        sum[u] = sum[pre] + 1;
        return tmp;
    }
    LL query(int u,int v,int x,int dep){
        if (dep < 0) return 0;
        LL t = x & bin[dep]; t >>= dep;
        if (sum[ch[u][t ^ 1]] - sum[ch[v][t ^ 1]])
            return bin[dep] + query(ch[u][t ^ 1],ch[v][t ^ 1],x,dep - 1);
        return query(ch[u][t],ch[v][t],x,dep - 1);
    }
}T;
int main(){
    bin[0] = 1; for (int i = 1; i <= Bit; i++) bin[i] = bin[i - 1] << 1;
    n = read(); m = read(); B = (int)sqrt(n) + 1;
    n++;
    for (int i = 2; i <= n; i++) A[i] = read();
    for (int i = 1; i <= n; i++){
        sum[i] = sum[i - 1] ^ A[i];
        T.rt[i] = T.ins(T.rt[i - 1],sum[i]);
        block[i] = i / B;
    }
    for (int i = 1; i <= n; i++){
        if (i == 1 || block[i] != block[i - 1]){
            int b = block[i];
            for (int j = i; j <= n; j++){
                f[b][j] = max(f[b][j - 1],T.query(T.rt[j - 1],T.rt[i - 1],sum[j],Bit));
            }
        }
    }
    n--;
    LL l,r,x,y;
    while (m--){
        x = read(); y = read();
        l = min (((x + lans) % n) + 1, ((y + lans) % n) + 1);
        r = max (((x + lans) % n) + 1, ((y + lans) % n) + 1) + 1;
        lans = 0;
        if (block[l] != block[r]) lans = f[block[l] + 1][r];
        for (int i = l; block[i] == block[l] && i < r; i++){
            lans = max(lans,T.query(T.rt[r],T.rt[i],sum[i],Bit));
        }
        printf("%lld\n",lans);
    }
    return 0;
}

BZOJ2741 【FOTILE模拟赛】L 【可持续化trie + 分块】

标签:lag   print   algo   开头   c++   inline   位置   正整数   开始   

原文地址:https://www.cnblogs.com/Mychael/p/8711218.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!