前言
找原题未遂了()
\(\rm{HD0X}\) 大佬讲了没听懂啊
思路
无敌了, 看起来似乎很困难, 不知道补不补的掉
首先发现不好处理成一种简单的问题, 肯定是想有哪些方法可以处理这种问题
\(\rm{TJ}\) 的不太看得懂
- 你可以树状数组维护区间和, 每次对于一个环暴力修改 \(\mathcal{O} (sz \log n)\), 然后查询 \(\mathcal{O} (\log n)\)
- 你也可以对一个环标记其旋转次数, 这样子做修改是 \(\mathcal{O} (1)\) 的, 查询比较复杂:
首先我们对于每个环计算其对区间的贡献, 具体的, 二分查找这个环的贡献区间, 然后常规计算
那么这样子查询是 \(\mathcal{O} (m \log sz)\)
现在我们已经有了这两种做法, 你发现这两种做法完全不同阶, 第一种做法 \(sz\) 影响最大, 第二种做法 \(m\) 影响最大, 考虑根号分治
所以设阈值 \(B\) , 假设低于阈值使用方法 \(1\) , 否则使用方法 \(2\) , 因为 \(sz\) 足够大时, 方法 \(2\) 的查询复杂度将被压缩
你发现现在的总时间复杂度为 \(\mathcal{O} (B \log n + \frac{n}{B} \log n)\) (假设 \(n, m, q\) 同阶)
令 \(B = \sqrt{n}\) , 时间复杂度达到最优, 为 \(\mathcal{O} (n \sqrt{n \log^2 n})\)
这什么神经复杂度, 算了一下达到了 \(9 \times 10^8\) , 肯定是搞不过去的
啊, 过啦
考虑优化, 但是不用也能过, 参考 题解
实现
框架
写的清楚一点, 这是码力题 :
首先处理每一个环的大小 (根据阈值分开)
对于每次修改, 小环直接暴力旋转, 大环打个标记
对于每次查询, 小环直接树状数组启动, 对于大环单独计算贡献
代码
参考 \(\rm{KLR}\) 大佬的代码
#include "iostream"
#include "algorithm"
using namespace std;
#define int long long
#define lowbit(x) x & -x
//#define getchar getchar_unlocked
template <class T>
void read(T &x) {
x = 0; char ch = getchar();
while (ch < '0' or ch > '9') ch = getchar();
while (ch >= '0' and ch <= '9') x = x * 10 + ch - 48, ch = getchar();
}
void print(int x) {
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
constexpr int N = 1.5e5 + 10, B = 666;
int n, m, Q;
int a[N], tg[N];
basic_string<int> c[N], pre[N], ex;
class Binary_Indexed_Tree {
protected:
int tr[N];
public:
void update(int x, int k) { for (; x <= n; x += lowbit(x)) tr[x] += k; }
int query(int x) {
int ret = 0;
for (; x; x -= lowbit(x)) ret += tr[x];
return ret;
}
int query(int l, int r) { return query(r) - query(l - 1); }
} bit;
void init() {
read(n), read(m), read(Q);
for (int i = 1, x; i <= n; ++i) read(x), c[x].push_back(i);
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i <= m; ++i) {
if (c[i].size() <= B) for (int x : c[i]) bit.update(x, a[x]);
else {
ex.push_back(i);
int tmp = 0;
for (int x : c[i]) pre[i].push_back(tmp += a[x]);
}
}
}
int deal(int x, int l, int r) {
l = lower_bound(c[x].begin(), c[x].end(), l) - c[x].begin(), r = upper_bound(c[x].begin(), c[x].end(), r) - c[x].begin() - 1;
if (l >= (int)c[x].size() or r < l) return 0;
l = (l + c[x].size() - tg[x]) % c[x].size(), r = (r + c[x].size() - tg[x]) % c[x].size();
if (l > r) return *pre[x].rbegin() - (pre[x][l - 1] - pre[x][r]);
return pre[x][r] - (l ? pre[x][l - 1] : 0);
}
void swp(int x) {
if (c[x].empty()) return;
for (int i : c[x]) bit.update(i, -a[i]);
bit.update(c[x][0], a[c[x][c[x].size() - 1]]);
for (int i = c[x].size() - 1; i; --i) bit.update(c[x][i], a[c[x][i - 1]]);
for (int i = c[x].size() - 1; i; --i) swap(a[c[x][i]], a[c[x][i - 1]]);
}
void calculate() {
while (Q--) {
int op, l, r;
read(op), read(l);
if (op & 1) {
read(r);
int ans = bit.query(l, r);
for (int x : ex) ans += deal(x, l, r);
print(ans), putchar('\n');
}
else {
if (c[l].size() <= B) swp(l);
else ++tg[l];
}
}
}
void solve() {
init();
calculate();
}
signed main() {
solve();
return 0;
}
总结
这个题的 \(\rm{subtask}\) 提示的非常完备, 挨着想肯定能想出来
根号分治无敌, 这个题属于超级巨大根号分治, 害怕
多种做法可以用根号分治均摊, 前提是需要有可以分治的点
标签:pre,ch,log,int,T4,01.03,mathcal,CW,size From: https://www.cnblogs.com/YzaCsp/p/18651449