【BZOJ2741】【块状链表+可持久化trie】FOTILE模拟赛L

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

HINT

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

Source

By seter

【分析】

第一次写可持久化trie,还有点问题。

题解网上都是...先不说了

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cstring>
  5 #include <vector>
  6 #include <utility>
  7 #include <iomanip>
  8 #include <string>
  9 #include <cmath>
 10 #include <queue>
 11 #include <assert.h>
 12 #include <map>
 13
 14 const int N = 12000 + 10;
 15 const int SIZE = 111;//块状链表的根号50000
 16 const int M = 50000 + 5;
 17 using namespace std;
 18 typedef long long ll;
 19 struct Node{
 20        int val;//代表数量
 21        int num;//代表值
 22        Node *ch[2];
 23 }mem[N * 31 * 10], *root[N];
 24 int n, m;//n为数量,m为操作次数
 25 struct BLOCK_LIST{//块状链表
 26        int data[SIZE];
 27        int size, next;
 28        void init(){
 29             size = 0;
 30             memset(data, 0, sizeof(data));
 31             next = -1;
 32        }
 33 }list[SIZE];
 34 int tot = 0;//记录mem使用空间
 35 int Max[SIZE + 10][SIZE + 10], Pos;//表示从i到j块的最大异或值
 36 int data[N];
 37
 38 Node *NEW(){//创建新trie节点
 39      Node *p = &mem[tot++];
 40      p->val = p->num = 0;
 41      p->ch[0] = p->ch[1] = NULL;
 42      return p;
 43 }
 44 void insert(Node *&p, Node *&last, int x){//k为根
 45      p = NEW();
 46
 47      Node *u = p, *a = last;
 48      for (int i = 30; i >= 0 ; i--){
 49          int t = (((1 << i) & x) == 0 ? 0 : 1);
 50          if (u->ch[t] == NULL){
 51             u->ch[t] = NEW();
 52             u->ch[t]->val = t;
 53             u->ch[t]->num = a->ch[t]->num + 1;
 54          }
 55          u->ch[t ^ 1] = a -> ch[t ^ 1];
 56          u = u -> ch[t];
 57          a = a -> ch[t];
 58      }
 59      return;
 60 }
 61 int find(Node *&a, Node *&b, int val){
 62     int Ans = 0;
 63     Node *x = a, *y = b;
 64     for (int i = 30; i >= 0; i--){
 65
 66         int t = ((((1 << i) & val) == 0 ? 0 : 1) ^ 1);
 67         if (x->ch[t] == NULL || (x->ch[t]->num - y->ch[t]->num) <= 0) t = (t ^ 1);
 68         Ans += (1 << i) * t;
 69         x = x->ch[t];
 70         y = y->ch[t];
 71     }
 72     //Ans += t;
 73     return Ans;
 74 }
 75
 76 void prepare(){
 77      memset(Max, 0, sizeof( Max ));
 78      Pos = 0;//Pos为块状链表的标号
 79      list[Pos++].init();
 80      insert(root[1], root[0], 0);//插入可持久化trie
 81      for (int cur = 0, i = 1; i <= n; cur = list[cur].next){
 82          int j, M = 0;//M用来记录块的最大值
 83          for (j = 0; j < SIZE && i <= n; i++, j++){
 84              list[cur].data[j] = data[i];
 85              list[cur].size++;
 86              insert(root[i + 1], root[i], data[i]);//插入可持久化trie
 87              int M2 = data[i];
 88              //M2 = find(root[i + 1], root[cur * SIZE], list[cur].data[j]);
 89              //printf("%d\n", M2);
 90              //if (M2 == data[i]) M2 = 0;//显然如果是它自己不如不加即直接从开头一直异或到i
 91              //Max[cur][cur] = M2;
 92              for (int k = Pos - 1; k >= 0; k--){
 93                  int tmp = find(root[i + 1], root[k * SIZE], data[i]);
 94                  //if (tmp == data[i]) tmp = 0;
 95                  if ((M2 ^ data[i]) < (tmp ^ data[i])) M2 = tmp;
 96                  Max[k][cur] = max(Max[k][cur], M2 ^ data[i]);//顺便利用O(sqrt(n))的时间预处理出Max数组
 97              }
 98          }
 99          //创建新块
100          if (j == SIZE){
101             list[Pos].init();
102             list[cur].next = Pos++;
103          }
104      }
105      //printf("%d\n", root[1]->ch[0]->ch[1]->num);
106 }
107 int query(int l, int r){
108      int x = (l - 1) / SIZE, y = (r - 1) / SIZE;//x代表l和r所代表的块
109      int Ans = 0;
110      if (x == y){
111            for (int i = l; i <= r; i++)
112                Ans = max(Ans, find(root[r + 1], root[l - 1], data[i]) ^ data[i]);
113            return Ans;
114      }else{
115            if (x  <= y - 1) Ans = Max[x ][y - 1];
116            //for (int i = r; i >= l; i--) if ((data[i] ^ data[i - 1]) == 32767) printf("fuck");
117            for (int i = l; i <= ((x + 1) * SIZE) && i <= n; i++) Ans = max(Ans, find(root[r + 1], root[l - 1], data[i]) ^ data[i]);
118            for (int i = r; i > y * SIZE; i--) Ans = max(Ans, find(root[r + 1], root[l - 1], data[i]) ^ data[i]);
119            return Ans;
120            //for (int i = l; i <= r; i++)
121            //    Ans = max(Ans, find(root[r + 1], root[l - 1], data[i]) ^ data[i]);
122            //return Ans;
123      }
124 }
125 //处理询问
126 void work(){
127      int last_ans = 0;
128      for (int i = 1; i <= m; i++){
129          int x, y, l, r;
130          scanf("%d%d", &l, &r);
131          //l--;
132          //l = min(((ll)((ll)x + (ll)last_ans) % n) + 1 , ((ll)((ll)y + (ll)last_ans) % n)+ 1);
133          //r = max(((ll)((ll)x + (ll)last_ans) % n) + 1 , ((ll)((ll)y + (ll)last_ans) % n)+ 1);
134          last_ans = query(l, r);
135          printf("%d\n", last_ans);
136      }
137 }
138 //单纯的插入一个数
139 void build(Node *&b, int x){
140      Node *u = b;
141      for (int i = 30; i >= 0; i--){
142          int t = (((1 << i) & x) == 0 ? 0 : 1);//表示这一位是否是0
143          if (u->ch[t] == NULL){
144             u->ch[t] = NEW();
145             u->ch[t]->val = t;
146             u->ch[t]->num = 0;//注意,这里仅仅只是建树,所以不能改数字
147          }
148          u = u->ch[t];
149      }
150      return;
151 }
152 void init(){
153      //读入+建初始树
154      scanf("%d%d", &n, &m);
155      for (int i = 0; i < N; i++) root[i] = NULL;
156      root[0] = NEW();
157      data[0] = 0;
158      for (int i = 1; i <= n; i++){
159          scanf("%d", &data[i]);
160          data[i] = data[i] ^ data[i - 1];
161          build(root[0], data[i]);
162          //printf("%d\n", data[i]);
163      }
164      build(root[0], 0);//记得加0
165      //printf("%d", root[0]->val);
166 }
167 void debug(){
168      insert(root[1], root[0], 0);
169      insert(root[2], root[1], 1);
170      insert(root[3], root[2], 6);
171      printf("%d\n", find(root[3], root[1], 6));
172 }
173
174 int main(){
175     #ifdef LOCAL
176     freopen("data.txt",  "r",  stdin);
177     freopen("out.txt",  "w",  stdout);
178     #endif
179     init();
180     prepare();
181     work();
182     printf("%d\n", tot);
183     //debug();
184     return 0;
185 }

时间: 2024-10-08 14:10:34

【BZOJ2741】【块状链表+可持久化trie】FOTILE模拟赛L的相关文章

BZOJ2741[FOTILE模拟赛]L

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).

bzoj 2741: 【FOTILE模拟赛】L 分塊+可持久化trie

2741: [FOTILE模拟赛]L Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 1116  Solved: 292[Submit][Status] Description FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和. 即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=r. 为了体现在线操作,对于一个询问(x,y):

bzoj2741: 【FOTILE模拟赛】L

2741: [FOTILE模拟赛]L Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 2679  Solved: 766[Submit][Status][Discuss] Description FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和. 即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=r. 为了体现在线操作,对于一

【BZOJ】【2741】【FOTILE模拟赛】L

可持久化Trie+分块 神题……Orz zyf & lyd 首先我们先将整个序列搞个前缀异或和,那么某一段的异或和,就变成了两个数的异或和,所以我们就将询问[某个区间中最大的区间异或和]改变成[某个区间中 max(两个数的异或和)] 要是我们能将所有[l,r]的答案都预处理出来,那么我们就可以O(1)回答了:然而我们并不能. 一个常见的折中方案:分块! 这里先假设我们实现了一个神奇的函数ask(l,r,x),可以帮我们求出[l,r]这个区间中的数,与x最大的异或值. 我们不预处理所有的左端点,我

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

题目大意 给出一个序列,求[l, r]中的最大连续xor 和. 强制在线 思路 先把整个序列分成n  √  块,预处理每一块的开头到每个数字的最大连续xor 和.这个我们只需处理出前缀xor 和,之后用可持久化Trie树就可以搞定.这样询问的右边就是整块的了.剩下左边的随便暴力一下就能过了.. CODE #define _CRT_SECURE_NO_WARNINGS #include <cmath> #include <cstdio> #include <cstring>

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

题意: 区间内最大连续异或和 5点调试到现在....人生无望 但总算A掉了 一开始想错可持久化trie的作用了...可持久化trie可以求一个数与一个数集的最大异或和 做法比较明显,前缀和后变成选区间内两个元素异或最大 考虑分块,预处理$f[i][j]$第i块到第j块选两个元素异或最大 询问时两边用可持久化trie暴力,中间整块已经预处理了 可以发现预处理复杂度$O(N\sqrt{N}*30)$,必须要枚举块中元素来算,不如直接保存下来$f[i][j]$为第i块到第j个元素的答案 如果说有什么教

BZOJ 2741 【FOTILE模拟赛】L(可持久化trie)

http://www.lydsy.com/JudgeOnline/problem.php?id=2741 思路:我们先将a变成a的异或前缀,这样问题就变成了,在l-1到r区间内,找出i,j令a[i]^a[j]最大. 假如i是固定的,我们可以建一个可持久化trie,在l-1到r区间内贪心寻找最优,但是这题i和j都不是固定的,如果暴力枚举i,那时间复杂度最坏是m*n*logn. 因此我们考虑这样:将n个数字分块,预处理出数组f[i][j],代表从第i块的开头作为左端点固定,j为右端点,这里面能产生的

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

题目大意:给定一个序列,多次询问[l,r]中最大子序异或和 强制在线 一直RE的同学注意,本题的强制在线如果直接加会爆int导致调用数组下标为负 首先我们有一个转化 维护前缀异或和数组a[] 那么[l,r]中最大子序异或和就是a数组中[l-1,r]中任取两个数的最大异或值 然后分块处理 对于每块的第一个数a[i] 我们依次处理出对于所有的j>=i的[i,j]中的最大异或值 即s[i][j]=max(a[j]与i~j所有数中的最大异或值,s[i][j-1]) 然后对于每个询问[l,r],设l后面第

bzoj2741【FOTILE模拟赛】L

http://www.lydsy.com/JudgeOnline/problem.php?id=2741 分块或可持久化trie 可以先看看这个:高斯消元解XOR方程组 分块做法: 我们先求出前i个数的异或和,即b[i]=a[1]^a[2]^...^a[i],不失一般性,记b[0]=0. 那么a[i]^a[i+1]^...^a[j-1]^a[j]=b[j]^b[i-1]. 所以原问题变成在b[l-1...r]中任选2个数,使得异或和最大. 我们将0..N分成$\sqrt{N}$块,不妨记第i块的