CodeChef - LNDNCK 回滚莫队

链接

题意:

给你两个数组, 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

*/

你可能感兴趣的:(其他----莫队,莫队,回滚莫队)