【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 ).
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
1 4 3
0 1
0 1
4 3
Sample Output
5
7
7
7
7
HINT
HINT
N=12000,M=6000,x,y,Ai在signed longint范围内。
Source
【分析】
第一次写可持久化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 }