题解: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;
}

浙公网安备 33010602011771号