题解:uoj659【ULR #2】Picks loves segment tree IX
牛牛题。
题意:给出一个操作序列,操作有四种,按位与,按位或,按位异或,加一。现在问对于一个数 \(v\),经过 \([l,r]\) 这些操作,最后得到的第 \(t\) 位值为多少。强制在线。
做法:
好像说这个 trick 叫按位划分状态。
考虑如果没有这个加法就简单完了,这里先给出一个没有加法可以做到 \(O(n\log V)\) 预处理 \(O(1)\) 查询的做法,后面回用到。我们记第 \(t\) 位在第 \(i\) 次操作上一次被操作到的位置为 \(pre_{t,i}\)。那么询问区间 \([l,r]\) 时就考虑 \(pre_{t,r}\) 是否小于 \(l\),如果小于,那么我们直接考虑区间异或和和 \(v\) 异或上后的答案。否则因为我有一次覆盖操作,我就直接考虑覆盖后到 \(v\) 的答案。
那么考虑加一操作怎么处理,有一个想法是建 Trie 然后在字典树上做,但是强制在线,可持久 Trie 太诡异了。实际的做法完全不需要用到数据结构。
我们考虑,如果末尾有若干个 \(1\),那么我 \(+1\) 其实可以认为是对低位做了一个集体异或。对于第 \(i\) 位,我只在乎那些对我有用的 \(+1\) 操作。
所以考虑记 \(f_{i,j}\) 代表我现在的数末尾至少有 \(i\) 个 \(0\),也就是考虑第 \(i\) 位,从第 \(j\) 个操作开始,下一次第一次让我这个位被进位的位置。直接转移做不了,我们考虑记一个辅助数组 \(g_{i,j}\),表示考虑第 \(i\) 位,末尾恰好有 \(i\) 个 \(0\),即第 \(i\) 位其实是 \(1\),从第 \(j\) 个操作开始,下一次末尾至少有 \(i+1\) 个 \(0\) 的位置。为了方便,令 \(f_{i,j}\gets f_{i,j}+1,g_{i,j}\gets g_{i,j}+1\)。
转移先考虑 \(f\),如果 \([i,f_{i,j})\) 这些操作让 \(i+1\) 第 \(i\) 位进位了,也就是说,\([i,f_{i,j}-1)\) 这些操作让第 \(i\) 位为 \(1\),那么就会进位,\(f_{i,j} = f_{i-1,j}\),否则 \(f_{i,j} = g_{i - 1, f_{i - 1,{j}}}\)。
然后考虑 \(g\),如果 \([i,f_{i,j})\) 这些操作完第 \(i\) 位保持 \(1\),那就不会进位,\(g_{i,j} = g_{i, f_{i,j}}\),否则 \(g_{i,j}=f_{i,j}\)。
那么询问就考虑我先一直跳 \(g\),让末尾有 \(t\) 个 \(0\),如果没有那就意味着不会影响到第 \(t\) 位,直接当没有加法的做即可;否则我们需要在 \(t\) 这一层一直跳 \(f_{t,p}\) 直到 \(> r+1\),是 \(r+1\) 是因为我的 \(f\) 多加了 \(1\)。这个可以对每个 \(t\) 建树出来跳,可以用倍增或者树剖,后者空间会更小。
时间复杂度和空间复杂度都是一只 $\log $ 的。
一些实现细节(如果和我是写的差不多的,看好像有很多写法不同的):
-
因为 \(+1\) 一定对第 \(0\) 位有影响,不妨认为一定对第 \(0\) 位异或,需要特判掉第 \(0\) 位的一些东西。
-
仔细检查询问区间的边界,因为我对这个东西加一了所以很多时候需要减一。
-
注意判断加一操作是否会对我的这一位有影响,有些加一操作是不被认为有影响的,比如对第 \(0\) 位加一其实被认为直接异或了就不能产生贡献。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5, mod = 998244353;
unsigned int x[maxn];
int n, q, op, opt[maxn], pre[32][maxn], f[32][maxn], g[32][maxn];
unsigned sx[maxn];
int query(int l, int r, int org, int t, int f) {
if(r > n)
return 0;
if(l > r)
return org;
if(pre[t][r] < l)
return (((sx[r] ^ sx[l - 1]) >> t) & 1) ^ org ^ (f && opt[r] == 3);
return (((sx[r] ^ sx[pre[t][r]]) >> t) & 1) ^ opt[pre[t][r]] ^ (f && opt[r] == 3);
}
struct Tree {
int dfn[maxn], sz[maxn], rev[maxn], son[maxn], top[maxn], rt, fa[maxn], tot, k, dv[maxn];
vector<int> e[maxn];
void dfs1(int u) {
sz[u] = 1;
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
dfs1(v);
sz[u] += sz[v];
if(sz[son[u]] < sz[v])
son[u] = v;
}
}
void dfs2(int u, int t) {
top[u] = t; dfn[u] = ++tot, rev[tot] = u;
if(u != t)
dv[fa[u]] = query(u, fa[u] - 1, 0, k, 1);
if(!son[u])
return ;
dfs2(son[u], t);
if(pre[k][fa[u] - 1] < u && u != t)
dv[fa[u]] ^= dv[u];
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
if(v == son[u])
continue;
dfs2(v, v);
}
}
void build(int K) {
k = K;
rt = n + 2; fa[n + 2] = n + 2;
for (int i = 1; i <= n + 1; i++)
e[f[k][i]].push_back(i), fa[i] = f[k][i];
dfs1(rt);
dfs2(rt, rt);
}
int queryseg(int v, int l, int r, int k) {
return (pre[k][r - 1] < l ? v ^ dv[l] ^ dv[r] : dv[r]);
}
int queryt(int v, int l, int r) {
while(fa[top[l]] <= r + 1) {
v = query(top[l], fa[top[l]] - 1, queryseg(v, l, top[l], k), k, 1);
l = fa[top[l]];
}
int lx = dfn[top[l]] - 1, rx = dfn[l];
while(lx + 1 < rx) {
int mid = lx + rx >> 1;
if(rev[mid] <= r + 1)
rx = mid;
else
lx = mid;
}
return query(rev[rx], r, queryseg(v, l, rev[rx], k), k, 0);
}
} tree[32];
int key, bs = 1;
int main() {
// freopen("test.in", "r", stdin);
// freopen("std.out", "w", stdout);
cin >> n >> q >> op;
for (int i = 1; i <= n; i++) {
char c; cin >> c >> x[i];
opt[i] = (c == '&' ? 0 : (c == '|' ? 1 : (c == '^' ? 2 : 3)));
}
for (int i = 1; i <= n; i++)
sx[i] = sx[i - 1] ^ (opt[i] == 2 ? x[i] : (opt[i] == 3));
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 32; j++)
pre[j][i] = pre[j][i - 1];
if(opt[i] > 1)
continue;
if(opt[i] == 0) {
for (int j = 0; j < 32; j++) {
if(((x[i] >> j) & 1) == 0)
pre[j][i] = i;
}
}
else {
for (int j = 0; j < 32; j++) {
if(((x[i] >> j) & 1) == 1)
pre[j][i] = i;
}
}
// for (int j = 0; j < 32; j++)
// cout << pre[j][i] << " ";
// cout << endl;
}
for (int i = 0; i < 32; i++)
f[i][n + 1] = g[i][n + 1] = n + 2;
for (int i = n; i >= 1; i--) {
f[0][i] = (opt[i] == 3 ? i + 1 : f[0][i + 1]);
g[0][i] = (query(i, f[0][i] - 1, 1, 0, 0) ? g[0][f[0][i]] : f[0][i]);
// cout << f[0][i] << " " << g[0][i] << endl;
}
for (int j = 1; j < 32; j++) {
for (int i = n; i >= 1; i--) {
f[j][i] = (query(i, f[j - 1][i] - 1, 0, j - 1, j > 1) ? g[j - 1][f[j - 1][i]] : f[j - 1][i]);
g[j][i] = (query(i, f[j][i] - 1, 1, j, 1) ? g[j][f[j][i]] : f[j][i]);
// cout << i << " " << j << " " << f[j][i] << endl;
}
// cout << endl;
}
// cout << f[3][2] << endl;
for (int i = 0; i < 32; i++)
tree[i].build(i);
while(q--) {
unsigned int v;
unsigned int l, r, t; cin >> v >> l >> r >> t;
v = v + key; l = (l ^ key) % n, r = (r ^ key) % n; t = (t ^ key) % 32;
if(l > r)
swap(l, r);
// cout << v << " " << l << " " << r << " " << t << endl;
l++, r++;
int nw = l, pos = 0;
while(pos < t) {
if(query(l, nw - 1, (v >> pos) & 1, pos, 1)) {
if(g[pos][nw] <= r + 1) {
nw = g[pos][nw];
pos++;
}
else
break;
}
else
pos++;
}
int ans;
if(t == 0)
ans = query(l, r, v & 1, 0, 0);
else if(pos < t)
ans = query(l, r, v >> t & 1, t, 0);
else
ans = tree[t].queryt(query(l, nw - 1, v >> t & 1, t, 1), nw, r);
putchar(ans + '0');
key = (key + op * bs * ans) % mod;
bs = bs * 2 % mod;
}
return 0;
}
/*
7 5 0
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
3 0 4 0
3 0 4 1
3 0 4 2
3 0 4 3
3 3 3 2
*/

浙公网安备 33010602011771号