莫队
回滚莫队
当 \([x, y]\) 固定,考虑 \(b_i = [x\le a_i \le y]\)。答案就是 \([l, r]\) 中每一段极长连续 \(1\) 的 \(\sum \dbinom{len + 1}{2}\)
做法是值域上莫队,然后因为是排列,每次一个值域只会修改一个位置的值。考虑 \(0\to 1\) 是好处理的。
维护两个链表 \(pre, suf\),表示极长的包含 \(i\) 的段的开头和结尾。
对于序列分块,记录每个块,不包含开头末尾极长连续段贡献的其他贡献 mid。(如果整个串都是 1, mid[i] = 0)
pre 和 suf 的维护就是每次合并 \(p\) 两端的,然后比较繁琐。
\(1 \to 0\) 非常难,于是考虑回滚莫队撤销怎么做,我们考虑当进行别的操作时,用另一个数组继承原数组的值,然后操作完了,再标记回来,这样做,就不用考虑撤销的问题了。非常牛逼的 std 实现。
点击查看代码
// 僕らタイムフライヤー \
時を駆け上がるクライマー \
時のかくれんぼ \
はぐれっこはもういやなんだ
#include <bits/stdc++.h>
#include <assert.h>
using namespace std;
typedef long long ll;
typedef double db;
#define int long long
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define dd(x) cerr << #x" = " << x << endl;
inline int read() {
int x=0, v=1,ch=getchar();
while('0'>ch||ch>'9') {
if(ch=='-') v=0;
ch=getchar();
}while('0'<=ch&&ch<='9') {
x=(x*10)+(ch^'0');
ch=getchar();
}return v?x:-x;
}
const int MAX = 2e5 + 5;
int bl[MAX], L[MAX], R[MAX];
ll bm(int x) { return 1ll * x * (x + 1) / 2; }
template <const int SIZ>
struct point {
int rea[SIZ];
int vit[SIZ];
int t[SIZ];
int cnt = 1;
bool fvit = 0;
void Roll() { ++ cnt; }
void Vit() {fvit = 1, Roll(); }
void Rea() {fvit = 0; }
void Clr() { memset(rea, 0, sizeof(rea)); Roll(); }
int &operator [] (int i) {
if(fvit) {
if(cnt ^ t[i]) t[i] = cnt, vit[i] = rea[i];
return vit[i];
}
return rea[i];
}
};
struct Q{
int l, r, x, y, id;
Q() {}
Q(int _id) {
l = read(), r = read(), x = read(), y = read();
id = _id;
}
bool operator < (const Q & h) const {
return (bl[x] == bl[h.x] ? y < h.y : x < h.x);
}
} q[MAX];
int n, T;
int c[MAX];
int pc[MAX];
ll ret[MAX];
point<MAX> pre, nxt, mid;
int bf1(const Q & q) {
int ans = 0, cnt = 0;
for(int i = q.l; i <= q.r; ++ i) {
if(q.x <= c[i] && c[i] <= q.y) {
++ cnt;
}else {
// ans += bm(cnt);
cnt = 0;
}
ans += cnt;
}
return ans;
}
int bf2(const Q & q) {
int ans = 0;
for(int i = q.x; i <= q.y; ++ i) {
int p = pc[i];
if(q.l <= p && p <= q.r) {
ans -= bm(p - pre[p - 1]);
ans -= bm(nxt[p + 1] - p);
nxt[pre[p - 1]] = nxt[p + 1];
pre[nxt[p + 1]] = pre[p - 1];
ans += bm(nxt[p + 1] - pre[p - 1] + 1);
}
}
pre.Roll(), nxt.Roll();
return ans;
}
void add(int x) {
/*
修改时有原则:如果 p 不是块的左右端点,那么以后是不会再被用到的。
*/
int p = pc[x], b = bl[p];
if(p != L[b]) {
if(pre[p - 1] != L[b]) mid[b] -= bm(p - pre[p - 1]);
if(p == R[b]) {
nxt[p] = p;
pre[p] = pre[p - 1];
nxt[pre[p - 1]] = p;
}else {
nxt[pre[p - 1]] = nxt[p + 1];
pre[nxt[p + 1]] = pre[p - 1];
}
}
if(p != R[b]) {
if(nxt[p + 1] != R[b]) mid[b] -= bm(nxt[p + 1] - p);
if(p == L[b]) {
pre[p] = p;
nxt[p] = nxt[p + 1];
pre[nxt[p + 1]] = p;
}else {
nxt[pre[p - 1]] = nxt[p + 1];
pre[nxt[p + 1]] = pre[p - 1];
}
}
if(p != L[b] && p != R[b] && pre[p - 1] != L[b] && nxt[p + 1] != R[b]) {
mid[b] += bm(nxt[p + 1] - pre[p - 1] + 1);
}
}
signed main() {
n = read(); T = read();
for(int i = 1; i <= n; ++ i) {
c[i] = read();
pc[c[i]] = i;
}
int B = n / (sqrt(T * 2 / 3) + 1) + 1;
int LIM = (n - 1) / B + 1;
for(int i = 1; i <= LIM; ++ i) {
L[i] = R[i - 1] + 1;
R[i] = R[i - 1] + B; if(R[i] > n) R[i] = n;
for(int j = L[i]; j <= R[i]; ++ j) bl[j] = i;
}
for(int i = 0; i <= n + 1; ++ i) pre[i] = i + 1, nxt[i] = i - 1;
nxt.Vit(), pre.Vit(), mid.Vit();
int ec = 0;
for(int i = 1; i <= T; ++ i) {
Q x = Q(i);
if(x.r - x.l <= B * 2) ret[i] = bf1(x);
else if(x.y - x.x <= B * 2) ret[i] = bf2(x);
else q[++ ec] = x;
}
sort(q + 1, q + 1 + ec);
int pt = 1;
for(int t = 1; t < bl[n]; ++ t) {
int r = R[t];
nxt.Rea(), pre.Rea(), mid.Rea();
for(int i = 0; i <= n + 1; ++ i) pre[i] = i + 1, nxt[i] = i - 1;
mid.Clr();
while(pt <= ec && bl[q[pt].x] == t) {
const Q & nq = q[pt];
nxt.Rea(), pre.Rea(), mid.Rea();
while(r < nq.y) add(++ r);
nxt.Vit(), pre.Vit(), mid.Vit();
for(int j = nq.x; j <= R[t]; ++ j)
add(j);
ll ans = 0, cnt = 0;
for(int i = nq.l; i <= R[bl[nq.l]]; ++ i) {
if(nq.x <= c[i] && c[i] <= nq.y) ++ cnt;
else ans += bm(cnt), cnt = 0;
}
for(int i = bl[nq.l] + 1; i < bl[nq.r]; ++ i) {
if(nxt[L[i]] == R[i]) cnt += R[i] - L[i] + 1;
else {
cnt += nxt[L[i]] - L[i] + 1;
ans += bm(cnt) + mid[i];
cnt = R[i] - pre[R[i]] + 1;
}
}
for(int i = L[bl[nq.r]]; i <= nq.r; ++ i) {
if(nq.x <= c[i] && c[i] <= nq.y) ++ cnt;
else ans += bm(cnt), cnt = 0;
}
ans += bm(cnt);
ret[nq.id] = ans;
nxt.Roll(), pre.Roll(), mid.Roll();
++ pt;
}
}
for(int i = 1; i <= T; ++ i) printf("%lld\n", ret[i]);
return 0;
}