「CF1100F」Ivan and Burgers - 线性基 - 分治

给定长度为nn的序列aia_i,有次qq询问(l,r)(l,r),求在中选取任意个,使得它们的异或和最大

n500000,ai106n\le 500000, a_i\le 10^6

CF1100F

Solution

考虑分治,把序列和询问一起分治

类似于树上点分治的做法,每次只计算跨区间的答案

对于当前分治区间,处理出以midmid为中心,分别向两边扩展的前/后缀线性基([l,mid)[l, mid)的后缀和(mid,r](mid, r]的前缀),询问的时候直接合并起来就行了

总复杂度O(nlog2n)O(n\log^2 n),好想好写好调也跑得过

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <bits/stdc++.h>

#define x first
#define y second
#define y1 Y1
#define y2 Y2
#define mp make_pair
#define pb push_back

using namespace std;

typedef long long LL;
typedef pair <int, int> pii;

template <typename T> inline int Chkmax (T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0; }
template <typename T> inline T read ()
{
T sum = 0, fl = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1;
for (; isdigit(ch); ch = getchar()) sum = (sum << 3) + (sum << 1) + ch - '0';
return sum * fl;
}

inline void proc_status ()
{
ifstream t ("/proc/self/status");
cerr << string (istreambuf_iterator <char> (t), istreambuf_iterator <char> ()) << endl;
}

const int Maxn = 5e5 + 100, Maxlen = 20;

int N, A[Maxn], M;

struct info {int x, y, id;};

struct Basis
{
int A[Maxlen + 2];

inline void init () { memset(A, 0, sizeof A); }

inline int insert (int x)
{
for (int i = Maxlen; ~i; --i)
{
if (!(x & (1 << i))) continue;
if (!A[i])
{
for (int j = 0; j < i; ++j) if (x & (1 << j)) x ^= A[j];
for (int j = i + 1; j <= Maxlen; ++j) if (A[j] & (1 << i)) A[j] ^= x;
A[i] = x;
return 1;
}
x ^= A[i];
}
return 0;
}

friend Basis merge (const Basis &a, const Basis &b)
{
Basis res = a;
for (int i = 0; i <= Maxlen; ++i) if (b.A[i]) res.insert(b.A[i]);
return res;
}

inline int query (int ans = 0)
{
for (int i = Maxlen; i >= 0; --i) Chkmax(ans, ans ^ A[i]);
return ans;
}

}B[Maxn];

int Ans[Maxn];
info Q[Maxn], Q1[Maxn], Q2[Maxn];

inline void Divide (int l, int r, int L, int R)
{
if (L > R) return ;
if (l == r)
{
for (int i = L; i <= R; ++i) Ans[Q[i].id] = A[l];
return ;
}

int mid = l + r >> 1, len1 = 0, len2 = 0;
B[mid].init(), B[mid].insert (A[mid]);
for (int i = mid - 1; i >= l; --i) B[i] = B[i + 1], B[i].insert (A[i]);
for (int i = mid + 1; i <= r; ++i) B[i] = B[i - 1], B[i].insert (A[i]);

for (int i = L; i <= R; ++i)
{
if (Q[i].x <= mid)
{
if (Q[i].y > mid) Ans[Q[i].id] = merge (B[Q[i].x], B[Q[i].y]).query();
else Q1[++len1] = Q[i];
}
else Q2[++len2] = Q[i];
}

for (int i = 1; i <= len1; ++i) Q[L + i - 1] = Q1[i];
for (int i = 1; i <= len2; ++i) Q[L + len1 - 1 + i] = Q2[i];

Divide (l, mid, L, L + len1 - 1), Divide (mid + 1, r, L + len1, L + len1 + len2 - 1);
}

inline void Solve ()
{
Divide(1, N, 1, M);
for (int i = 1; i <= M; ++i) printf("%d\n", Ans[i]);
}

inline void Input ()
{
N = read<int>();
for (int i = 1; i <= N; ++i) A[i] = read<int>();
M = read<int>();
for (int i = 1; i <= M; ++i) Q[i].x = read<int>(), Q[i].y = read<int>(), Q[i].id = i;
}

int main()
{
#ifndef ONLINE_JUDGE
freopen("F.in", "r", stdin);
freopen("F.out", "w", stdout);
#endif
Input();
Solve();
return 0;
}
感谢你的赞赏!