链接
给你两个数组, B, P, 数组个数n 小于等于 2e5.
m 个询问, 每次询问 l r, 把 区间 [l, r] 按照 b 的升序排序, 然后求和 abs(p[i] - p[i-2]).
一开始的思路就是直接暴力莫队,
每次把 b 插入到map 里面去, 删除也是直接从 map 里面删除。
每次修改只会影响周围的几个值。
但是每次map 的查询是 log 的, 会超时。
所以要想一个方法, 每次查询时 O(1) 的。
这个时候用回滚莫队。
用一个链表把 b 从小到大串起来。 只有 删除的操作, 然后每次回滚。
在操作每一个块的时候, r 设置成 n, l 设置成 块的左端点。
然后 r 不断减小, 每次询问结束,l 会重新回滚到 块的左端点。
删除一个节点就是把这个节点的左右节点连起来。
回滚一个节点就是在把这个节点加进去。
这里有个不开结构体然后排序的东西,
就是 B 数组, P 数组,要跟着B数组排序。
直接另开一个 index 数组, 用下标排序, cmp 里面写这 B 的关系就好了.
inline bool cmp(int i, int j){
return B[i] < B[j];
}
for (Re i = n; i >= 1; --i){
int p = P[idx[i]]; // 直接这样子使用就可以了.
}
一开始用的时链表, 每次 new , 很废时间。
用链表的时候,用 unordered_map 存的每一个节点。
后来弃了链表,直接用数组模拟链表。 也没有用 map 存node 的位置。
分块的时候, 用的时 n 的 2/3 次方, tmd, 这就是个坑, 应该用sqrt(n), 博客害人。
#include
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(register int i = a; i <= b; i++)
#define per(i,a,b) for(register int i = a; i >= b; i--)
#define Re register int
typedef long long ll;
typedef double db;
const int N = 2e5+100;
using namespace std;
int a[N], pos[N], n, m, L[N], R[N], B[N], P[N],blocks,idx[N];
ll ans[N], Ans;
struct Node {
int l,r,id;
bool operator < (Node xx) const {
if(pos[l] ^ pos[xx.l]) return pos[l] < pos[xx.l];
return r > xx.r;
}
}Q[N];
struct node{
int val;
int pre,nxt;
}f[N];
inline int read(){
int x=0;char ch=getchar();
while(ch>'9'||ch<'0')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
void out(ll x){
if(x>9)out(x/10);
putchar(x%10+'0');
}
inline bool cmp(int i, int j){
return B[i] < B[j];
}
void init() {
n = read();
int sz = sqrt(n);
blocks = n / sz;
for (Re i = 1; i <= blocks; ++i)
L[i] = (i-1)*sz + 1, R[i] = i * sz;
if (R[blocks] < n) {
blocks++;
L[blocks] = R[blocks-1] + 1;
R[blocks] = n; // 块的边界。
}
for (Re i = 1; i <= blocks; ++i)
for (Re j = L[i]; j <= R[i]; ++j)
pos[j] = i; // 为每个位置分配块。
rep(i,1,n) {
B[i] = read();P[i] = read();
idx[i] = i;
}
idx[n+1] = n+1;
Ans = 0;
sort(idx+1,idx+n+1,cmp);
f[0].val = 0;
f[n+1].val = -1;
for (Re i = n; i >= 1; --i){ // 处理链表。
f[idx[i]].val = P[idx[i]];
f[idx[i]].nxt = idx[i+1];
f[idx[i]].pre = idx[i-1];
if (i > 2) Ans += abs(P[idx[i]] - P[idx[i-2]]);
}
m = read();
rep(i,1,m) { // 处理查询
Q[i].l = read();
Q[i].r = read();
Q[i].id = i;
}
sort(Q+1,Q+1+m);
}
void add(int x) {
node tmp = f[x];
f[tmp.nxt].pre = x;
f[tmp.pre].nxt = x;
}
void del(int x, ll &tmp) {
node it1 = f[x];
node it2 = it1;
node it3 = it1;
x = it1.val;
int x1 = -1, x2 = -1, x3 = -1, x4 = -1;
if( f[it1.pre].val != f[0].val) {
it1 = f[it1.pre];
x2 = it1.val;
}
if(f[it1.pre].val != f[0].val) {
it1 = f[it1.pre];
x1 = it1.val;
}
it2 = f[it2.nxt];
if(it2.val != f[n+1].val) {
x3 = it2.val;
it2 = f[it2.nxt];
}
if(it2.val != f[n+1].val) x4 = it2.val;
if(x1 != -1) tmp -= (ll)abs(x - x1);
if(x4 != -1) tmp -= (ll)abs(x - x4);
if(x3 != -1 && x2 != -1) tmp -= (ll)abs(x3-x2);
if(x1 != -1 && x3 != -1) tmp += (ll)abs(x1-x3);
if(x4 != -1 && x2 != -1) tmp += (ll)abs(x4-x2);
f[it3.nxt].pre = it3.pre;
f[it3.pre].nxt = it3.nxt;
}
void solve() {
int lastblock = 0,l = 1,r = n;
ll ans1,ans2;
rep(i,1,m) {
if (pos[Q[i].l] ^ lastblock){
while(r < n) add(++r);
while(l < L[pos[Q[i].l]]) del(l++, Ans); // 移动到下一个块的时候, Ans 会改变。
lastblock = pos[Q[i].l];
ans1 = Ans; // ans1 是 r 不断减小的时候更新答案。
}
while(r > Q[i].r) del(r--, ans1);
ans2 = ans1; // ans1 的值不能动, 因为询问之后, l 会重新回退到这个位置,
while(l < Q[i].l) {
del(l++, ans2);
}
ans[Q[i].id] = ans2;
while(l > L[pos[Q[i].l]]) add(--l);
}
rep(i,1,m) out(ans[i]),puts("");
}
int main(){
init();
solve();
return 0;
}
/*
6
2 1
4 6
1 4
3 10
6 4
5 8
5
1 2
1 3
1 4
1 5
1 6
2
15
10
8
0
*/