题解:uoj671【UNR #5】诡异操作
饺子醋环节。
题意:给出一个 \(n\) 长序列 \(a\),有三种操作:
-
区间除法。
-
区间取与。
-
区间求和。
\(n\le 2\times 10^5,V< 2^{128}\)。题面给了一份输入输出模板。
做法:
首先直接做考虑每个点维护一下 \(2^k\) 出现了多少次,一操作直接做总复杂度是 \(O(n\log V)\) 的,二操作采用直接打 tag,复杂度 \(O(n\log n)\),pushup 是 \(\log V\) 的,所以总复杂度是 \(O(n\log^2V)\)。
考虑怎么优化,除法和 tag 感觉没什么可以优化的了,考虑怎么优化 pushup。观察 pushup 的本质是什么,我们把 \(2^k\) 出现次数展开成二进制写,其实我们是维护了一个 \(\log V\times \log n\) 的矩阵,那么我们其实没有必要去维护 \(\log V\) 这边,而是维护 \(\log n\) 这一边即可,复杂度降到 \(n\log n\log V\),可以通过。没有太看懂网上题解对于除法说精细实现可以做到 \(n\log V\) 的解释,我一直整体除以 \(2\) 不是爆炸完了。
有点卡常,一个可行的卡常方法是把每个节点只考虑前若干个有值得行,这样会快特别多。我这里比较暴力,直接把长度补成 \(2^n\) 然后就比较方便定长度。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define u128 __uint128_t
const int maxn = 5.3e5 + 5;
typedef __uint128_t u128;
inline u128 read() {
static char buf[100];
scanf("%s", buf);
// std::cin >> buf;
u128 res = 0;
for(int i = 0;buf[i];++i) {
res = res << 4 | (buf[i] <= '9' ? buf[i] - '0' : buf[i] - 'a' + 10);
}
return res;
}
inline void output(u128 res) {
if(res >= 16)
output(res / 16);
putchar(res % 16 >= 10 ? 'a' + res % 16 - 10 : '0' + res % 16);
//std::cout.put(res % 16 >= 10 ? 'a' + res % 16 - 10 : '0' + res % 16);
}
struct node {
vector<u128> a;
bool f;
inline u128& operator[](int x) {
return a[x];
}
void resize(int N) {
a.resize(N);
}
inline int size() {
return a.size();
}
} ;
int n, q;
u128 a[maxn], mx, pw = 1;
int len[maxn];
struct Segtree {
node tr[maxn << 2];
u128 tag[maxn << 2];
void pushup(int t) {
tr[t].f = tr[t << 1].f & tr[t << 1 | 1].f;
u128 val = 0;
for (int i = 0; i < tr[t << 1].size(); i++) {
tr[t][i] = val ^ tr[t << 1][i] ^ tr[t << 1 | 1][i];
val = (tr[t << 1][i] & tr[t << 1 | 1][i]) | (tr[t << 1][i] & val) | (val & tr[t << 1 | 1][i]);
}
tr[t][tr[t].size() - 1] = val;
}
void build(int l, int r, int t) {
tag[t] = mx;
tr[t].resize(len[r - l + 1]);
//cout << l << " " << r << "asdf " << len[r - l + 1] << endl;
if(l == r) {
tr[t][0] = a[l];
tr[t].f = a[l] == 0;
return ;
}
int mid = l + r >> 1;
build(l, mid, t << 1), build(mid + 1, r, t << 1 | 1);
pushup(t);
}
void addtag(int t, u128 val) {
tag[t] &= val;
for (int i = 0; i < tr[t].size(); i++)
tr[t][i] &= val;
}
void pushdown(int t) {
if(tag[t] == mx)
return ;
addtag(t << 1, tag[t]);
addtag(t << 1 | 1, tag[t]);
tag[t] = mx;
}
void update1(int l, int r, int x, int y, int t, u128 v) {
if(tr[t].f)
return ;
if(l == r) {
tr[t][0] /= v;
tr[t].f = tr[t][0] == 0;
return ;
}
int mid = l + r >> 1;
pushdown(t);
if(x <= mid)
update1(l, mid, x, y, t << 1, v);
if(mid < y)
update1(mid + 1, r, x, y, t << 1 | 1, v);
pushup(t);
}
void update2(int l, int r, int x, int y, int t, u128 v) {
if(tr[t].f)
return ;
if(x <= l && r <= y) {
addtag(t, v);
return ;
}
int mid = l + r >> 1;
pushdown(t);
if(x <= mid)
update2(l, mid, x, y, t << 1, v);
if(mid < y)
update2(mid + 1, r, x, y, t << 1 | 1, v);
pushup(t);
}
u128 query(int l, int r, int x, int y, int t) {
if(x <= l && r <= y) {
u128 res = 0;
for (int i = 0; i < tr[t].size(); i++)
res += tr[t][i] << i;
return res;
}
int mid = l + r >> 1;
pushdown(t);
if(y <= mid)
return query(l, mid, x, y, t << 1);
if(mid < x)
return query(mid + 1, r, x, y, t << 1 | 1);
return query(l, mid, x, y, t << 1) + query(mid + 1, r, x, y, t << 1 | 1);
}
} tree;
signed main() {
cin >> n >> q;
for (int i = 1; i <= n; i++)
a[i] = read();
for (int i = 0; i < 128; i++)
mx += pw, pw <<= 1;
int tn = 1, cnt = 1;
while(tn < n)
len[tn] = cnt, tn <<= 1, cnt++;
len[tn] = cnt;
n = tn;
tree.build(1, n, 1);
while(q--) {
int op, l, r; u128 v;
cin >> op >> l >> r;
if(op == 1) {
v = read();
if(v != 1)
tree.update1(1, n, l, r, 1, v);
}
if(op == 2)
v = read(), tree.update2(1, n, l, r, 1, v);
if(op == 3)
output(tree.query(1, n, l, r, 1)), putchar('\n');
}
return 0;
}

浙公网安备 33010602011771号