这题怎么这么多差评啊?哦卡常啊,没事了。
发现两个操作都是只增不减,显然势能线段树。
考虑维护区间按位与,线段树上维护每一位上有多少个 \(1\),按位与就是区间赋 \(0\),对于区间除法暴力重构。直接暴力维护即可做到 \(O((n + q) \log n \log V)\) 的复杂度。但是 \(\log V = 128\),顶两个 \(\log\),哈哈!
考虑优化。注意到叶子处的数都很小,即数很多而值域很小。那么我们考虑按照值域维护,而不是按照每一位维护。我们将出现次数的每一位维护一个 int128
,合并时可以直接模拟二进制加法,区间赋 \(0\) 也可以直接维护一个标记实现,这样 pushUp
的复杂度变为了 \(O(\log len)\),而不是 \(O(\log V)\)。这样我们操作 \(2\) 的复杂度变为了 \(O(q \log^2 n)\),可以通过。但是操作 \(1\) 仍然是 \(O(n \log n \log V)\) 啊?实际上,进一步分析会发现操作 \(1\) 的总复杂度为 \(O(n \log V)\)。
具体来讲,就是考虑势能线段树的分析方法。简单设一个节点的势能函数为当前区间内 \(\log a_i\) 最大值。区间修改造成的贡献肯定是 \(O(q \log^2 n)\),对于非区间修改造成的贡献,一定会使得区间内所有 \(\log a_i\) 都减少 \(1\),那么最大值就会减小 \(1\)。而第 \(i\) 个点的 pushUp
复杂度为 \(O(\log len_i)\),这样复杂度总和就应该为 \(O(\log V \sum \log len_i)\)。对于 \(T(n) = \sum \log a_i\) 我们有 \(T(n) = 2T(\frac{n}{2}) + O(\log n)\),主定理或打表可知 \(T(n) = \Theta(n)\),那么这样这部分的复杂度就是 \(O(n \log V)\) 了。
于是总复杂度就是 \(O(n \log V + q \log^2 n)\)。
然后 注 意 常 数 优 化 。(比如不要维护区间和,等查询的时候再直接求;用 vector
减少内存不连续访问)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 600005;
typedef __uint128_t u128;
const u128 UINT128_MAX = ((u128) ULLONG_MAX << 64 | ULLONG_MAX);
inline u128 read() {
static char buf[100];
scanf("%s", 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);
}
int n, q;
u128 a[MAXN];
vector<u128> val[MAXN << 2];
u128 tag[MAXN << 2];
bool empty[MAXN << 2];
#define lc (i << 1)
#define rc (i << 1 | 1)
__attribute__((always_inline)) inline void pushUp(int i) {
u128 x = 0;
auto &o = val[i], &p = val[lc], &q = val[rc];
empty[i] = empty[lc] && empty[rc];
for (int j = 0; j < p.size(); j++) {
o[j] = p[j] ^ q[j] ^ x;
x = (p[j] & x) | (q[j] & x) | (p[j] & q[j]);
}
o[p.size()] = x;
}
__attribute__((always_inline)) inline void addTag(int i, u128 x) {
tag[i] &= x;
for (int j = 0; j < val[i].size(); j++) val[i][j] &= x;
}
__attribute__((always_inline)) inline void pushDown(int i) {
while (~tag[i])
addTag(lc, tag[i]), addTag(rc, tag[i]), tag[i] = UINT128_MAX;
}
void build(int i = 1, int l = 1, int r = n) {
tag[i] = UINT128_MAX;
if (l == r) {
val[i].push_back(a[l]);
empty[i] = val[i][0] == 0;
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid), build(rc, mid + 1, r);
val[i].resize(val[lc].size() + 1);
pushUp(i);
}
void updateAnd(int qa, int qb, u128 qv, int i = 1, int l = 1, int r = n) {
if (empty[i]) return;
if (qa <= l && r <= qb) {
addTag(i, qv);
return;
}
int mid = (l + r) >> 1;
pushDown(i);
if (qa <= mid) updateAnd(qa, qb, qv, lc, l, mid);
if (qb > mid) updateAnd(qa, qb, qv, rc, mid + 1, r);
pushUp(i);
}
void updateDiv(int qa, int qb, u128 qv, int i = 1, int l = 1, int r = n) {
if (empty[i]) return;
if (l == r) {
empty[i] = (val[i][0] /= qv) == 0;
return;
}
int mid = (l + r) >> 1;
pushDown(i);
if (qa <= mid) updateDiv(qa, qb, qv, lc, l, mid);
if (qb > mid) updateDiv(qa, qb, qv, rc, mid + 1, r);
pushUp(i);
}
u128 sum(int qa, int qb, int i = 1, int l = 1, int r = n) {
if (qa <= l && r <= qb) {
u128 ret = 0;
for (int j = 0; j < val[i].size(); j++) {
ret += val[i][j] << j;
}
return ret;
}
int mid = (l + r) >> 1;
pushDown(i);
if (qb <= mid) return sum(qa, qb, lc, l, mid);
if (qa > mid) return sum(qa, qb, rc, mid + 1, r);
return sum(qa, qb, lc, l, mid) + sum(qa, qb, rc, mid + 1, r);
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
a[i] = read();
}
int m = n; n = 1;
while (n < m) n <<= 1;
build();
while (q--) {
int op, l, r; scanf("%d%d%d", &op, &l, &r);
if (op == 1) {
u128 x = read();
if (x != 1) updateDiv(l, r, x);
} else if (op == 2) {
updateAnd(l, r, read());
} else {
output(sum(l, r)), putchar('\n');
}
}
return 0;
}
标签:UNR,qb,log,int,UOJ671,mid,qa,解题,复杂度
From: https://www.cnblogs.com/apjifengc/p/17414272.html