题解:P4807 [CCC 2017] 地铁交通

一种使用分散层叠的在线 \(O(n)\) 空间,\(O(n \sqrt n)\) 时间做法。

题目大意

给定一堆环,有两个操作,一个是将一个环的所有元素值转一圈,另一个是查询区间和。

思路

这个题目初看并不好做,先考虑一下暴力,如果一个环很小,那么可以直接暴力修改。

但是环特别大的时候就爆炸了,而这时候注意到这些环的数量不会太多。

看上去很像根号分治。

  • 如果一个环大小小于 \(\sqrt n\),直接暴力修改。
  • 如果一个环大小大于等于 \(\sqrt n\),那么这些环至多有 \(\sqrt n\) 个。

小环的修改直接使用一些单点修改,区间查询的数据结构维护即可。

大环怎么修呢?

其实题目的环的操作本质上是对于这个序列的所有元素向后移动一,将最后一个放到最前面。

那么如果不考虑最后一个,其实等价于如果此时找到一个点,那么该点的真实值是该点向前一个点的值。

那最后一个呢?

其实只需要再复制一份就好啦。

那么这个环根本不需要修改,只需要打一个标记即可。

每次查询的时候二分查询单个环的区间和,然后把查到的区间按照标记平移到正确的位置就好了。

此时最优时间复杂度:\(\mathcal{O}(n\sqrt n \log n)\)

空间复杂度:\(\mathcal{O}(n)\)

优化

这个时间复杂度还是太劣了,而小环的修改是修改多查询少的,那么可以使用 \(\mathcal{O}(1)-\mathcal{O}(\sqrt n)\) 的分块。

此时最优复杂度:\(\mathcal{O}(n\sqrt{n\log n})\)

但是这个复杂度也还是太劣了,那么考虑继续优化。

发现其实大环查询出的位置是单调的,那么可以做到 \(O(n\sqrt n)\) 扫一遍。

不过这个空间太劣了,而转离线又太拉了,这是就这么任命了吗?

我命由我不由天!

注意一件事,就是现在的复杂度瓶颈在多个大环上二分,这不就是分散层叠吗?

此时就可以使用分散层叠做到 \(\mathcal{O}(n(\sqrt n + \log n))\)

这样总时间复杂度就是:\(\mathcal{O}(n\sqrt n + n\log n)\)

空间复杂度:\(\mathcal{O}(n)\)

常数就别问了。。。

代码

#include <iostream>
#include <map>
#include <vector>
#include <climits>
#include <algorithm>
#include <bitset>
#include <cmath>
#include <algorithm>
#include <bit>
#include <cassert>
#include <cstdint>
#include <cstring>
using namespace std;

#define fi first
#define se second
#define emp emplace_back

using ld = long double;
using ll = long long;
using pii = pair <int, ll>;

const int N = 1e5 + 5e4 + 10, B = 500, B2 = 400, p = 3;

int a[N], n, m, q, tag[N / B + 10], ids[N];
bitset <N> vis;
vector <int> G[N];
vector <pii> tmp[N / B + 10];
vector <int> BigG;

class SemTree
{
    #define lid id << 1
    #define rid id << 1 | 1
    public :

    int cnt[(N / B + 10) << 2];

    struct Node
    {
        int val;
        ll sum;
        int ls, rs;
    };
    Node *m[((N / B) + 10) << 2];

    void Build(int id, int l, int r)
    {
        if (l > r) return ;
        if (l == r)
        {
            int _id = BigG[l - 1];
            cnt[id] = tmp[ids[_id]].size();
            m[id] = new Node[cnt[id] + 4];
            m[id][0] = {0, 0, 0, 0};
            for (int i = 0; i < cnt[id] + 4; i++) m[id][i] = {0, 0, 0, 0};
            for (int i = 1; i <= cnt[id]; i++) m[id][i] = {tmp[ids[_id]][i - 1].fi + 1, tmp[ids[_id]][i - 1].se, 0, 0};
            m[id][++cnt[id]] = {0, m[id][cnt[id] - 1].sum, 0, 0};
            vector <pii>().swap(tmp[l]);
            return ;
        }
        int cl = 2, cr = 2, t = 0, mid = (l + r) >> 1;
        Build(lid, l, mid); Build(rid, mid + 1, r);
        m[id] = new Node[cnt[lid] + cnt[rid] + 4];
        while (cl < cnt[lid] && cr < cnt[rid])
        {
            if (m[lid][cl].val < m[rid][cr].val)
            {
                m[id][++t] = {m[lid][cl].val, m[lid][cl].sum, cl, cr};
                cl += p;
            }
            else
            {
                m[id][++t] = {m[rid][cr].val, m[rid][cr].sum, cl, cr};
                cr += p;
            }
        }
        while (cl < cnt[lid]) m[id][++t] = {m[lid][cl].val, m[lid][cl].sum, cl, cnt[rid]}, cl += p;
        while (cr < cnt[rid]) m[id][++t] = {m[rid][cr].val, m[lid][cl].sum, cnt[lid], cr}, cr += p;
        m[id][++t] = {0, m[id][t - 1].sum, cnt[lid], cnt[rid]};
        cnt[id] = t;
    }

    ll Query(int id, int l, int r, int pos, ll k)
    {
        if (l > r) return 0;
        while (pos && m[id][pos - 1].val >= k) --pos; // O(p)
        ll ans = 0, mid = (l + r) >> 1;
        if (l == r) return pos == 0 ? 0 : m[id][pos - 1 - tag[ids[BigG[l - 1]]]].sum;
        ans = Query(lid, l, mid, m[id][pos].ls, k) + Query(rid, mid + 1, r, m[id][pos].rs, k);
        return ans;
    }

    ll Query(ll t)
    {
        ll l = 1, r = cnt[1], pos = r;
        while (l <= r)
        {
            ll mid = (l + r) >> 1;
            if (m[1][mid].val < t) l = mid + 1;
            else r = mid - 1, pos = mid;
        }
        return Query(1, 1, BigG.size(), pos, t);
    }
}ST;

class ArrayTree
{
    public :
    
    ll c[N / B2 + 10];

    inline int b(int x)
    {
        return (x - 1) / B2 + 1;
    }

    inline void Update(int pos, int v)
    {
        c[b(pos)] += v;
    }

    ll Query(int l, int r)
    {
        int L = b(l), R = b(r);
        ll ans = 0;
        if (L == R)
        {
            for (int i = l; i <= r; i++) if (vis[i]) ans += a[i];
        }
        else
        {
            for (int i = l; i <= B2 * L; i++) if (vis[i]) ans += a[i];
            for (int i = L + 1; i < R; i++) ans += c[i];
            for (int i = B2 * (R - 1) + 1; i <= r; i++) if (vis[i]) ans += a[i];
        }
        return ans;
    }
}K;

ll Query(int l, int r, int i)
{
    ll _ans = ST.Query(2 * (r + 1)) - ST.Query(2 * l);
    return _ans;
}

signed main()
{
    ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++)
    {
        int x; cin >> x;
        G[x].emp(i);
    }
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= m; i++)
        if (G[i].size() > B)
        {
            BigG.emp(i);
            vector <int> _tmp;
            for (auto to : G[i]) _tmp.emp(to);
            ll sum = 0;
            ids[i] = ++ids[N - 1];
            for (auto to : G[i]) sum += a[to], tmp[ids[i]].emp(-1, sum);
            for (auto to : _tmp) sum += a[to], tmp[ids[i]].emp(2 * to, sum);
        }
    for (int i = 1; i <= m; i++)
        if (G[i].size() <= B)
            for (auto to : G[i]) K.Update(to, a[to]), vis[to] = 1;
    ST.Build(1, 1, BigG.size());
    for (int i = 1; i <= q; i++)
    {
        int op; cin >> op;
        if (op == 1)
        {
            int l, r; cin >> l >> r;
            ll ans = K.Query(l, r) + Query(l, r, 114514);
            cout << (int64_t)ans << '\n';
        }
        else
        {
            int x; cin >> x;
            if (G[x].size() > B) tag[ids[x]]++, tag[ids[x]] %= G[x].size();
            else
            {
                for (auto to : G[x]) K.Update(to, -a[to]);
                for (int i = G[x].size() - 1; i > 0; i--)
                    swap(a[G[x][i]], a[G[x][i - 1]]);
                for (auto to : G[x]) K.Update(to, a[to]);
            }
        }
    }

    return 0;
}
posted @ 2025-11-11 15:51  QEDQEDQED  阅读(8)  评论(1)    收藏  举报