「解题报告」UOJ671 [UNR #5] 诡异操作

这题怎么这么多差评啊?哦卡常啊,没事了。

发现两个操作都是只增不减,显然势能线段树。

考虑维护区间按位与,线段树上维护每一位上有多少个 \(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;
}
posted @ 2023-05-19 10:44  APJifengc  阅读(92)  评论(0编辑  收藏  举报