Mo's Algorithm with extra backtracking Problem
Difference between en1 and en2, changed 116 character(s)
I was trying to solve this problem on CodeChef, [QCHEF](https://www.codechef.com/problems/QCHEF), using Mo's algorithm and realized it is not so simple to come up with a data structure that can save previous results, so that you can simply obtain the result after deletion. I looked up how to maintain previous results, and found this nice comment, [link](https://codeforces.me/blog/entry/7383?#comment-161520), where it describes this abstract notion on how one could have one function, snapshot(), for saving the current results, and another function, rollback() to go back to the previous state. I tried to implement this, code below, but it failed miserably. Any help would be appreciated. ↵

UPD: Apparently I had a typo in one of the add functions, and fixing that immediately solved the problem lol. ↵


<spoiler summary="Code">↵

~~~~~↵
#pragma GCC optimize("O3")↵
#pragma GCC target("sse4")↵
#include <bits/stdc++.h>↵
using namespace std;↵

using ll = long long;↵
using vi = vector<int>;↵
using pi = pair<int, int>;↵
using vpi = vector<pair<int, int>>;↵
using pl = pair<ll, ll>;↵
using vl = vector<ll>;↵
using vpl = vector<pl>;↵
using ld = long double;↵

#define all(v) (v).begin(), (v).end()↵
#define ar array↵
#define pb push_back↵
#define sz(x) (int)(x).size()↵
#define fi first↵
#define se second↵
#define lb lower_bound↵

template <typename T>↵
using pqg = priority_queue<T, vector<T>, greater<T>>;↵
template <typename A, typename B>↵
ostream &operator<<(ostream &os, const pair<A, B> &p)↵
{↵
    return os << '(' << p.first << ", " << p.second << ')';↵
}↵
template <typename T_container, typename T = typename enable_if<!is_same<T_container, string>::value, typename T_container::value_type>::type>↵
ostream &operator<<(ostream &os, const T_container &v)↵
{↵
    os << '{';↵
    string sep;↵
    for (const T &x : v)↵
        os << sep << x, sep = ", ";↵
    return os << '}';↵
}↵
void dbg_out()↵
{↵
    cerr << endl;↵
}↵
template <typename Head, typename... Tail>↵
void dbg_out(Head H, Tail... T)↵
{↵
    cerr << ' ' << H;↵
    dbg_out(T...);↵
}↵
#ifdef LOCAL↵
#define dbg(...) cerr << "(" << #__VA_ARGS__ << "):", dbg_out(__VA_ARGS__)↵
#else↵
#define dbg(...)↵
#endif↵

struct custom_hash↵
{↵
    static uint64_t splitmix64(uint64_t x)↵
    {↵
        x += 0x9e3779b97f4a7c15;↵
        x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;↵
        x = (x ^ (x >> 27)) * 0x94d049bb133111eb;↵
        return x ^ (x >> 31);↵
    }↵

    size_t operator()(uint64_t x) const↵
    {↵
        static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();↵
        return splitmix64(x + FIXED_RANDOM);↵
    }↵
};↵

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());↵

const int INF = 1e9;↵
const ll LINF = 1e18;↵
const int MOD = 1e9 + 7; //998244353;↵
const int N = 1<<20;↵
int a[N], res=0, previous=0;↵
int mi[N], mx[N];↵
int leftmost[N], n, m, k;↵
vector<pair<int,pi>> mem;↵



void addleft(int idx){↵
mem.pb({0, {a[idx], mi[a[idx]]}});↵
mem.pb({1, {a[idx], mx[a[idx]]}});↵
mi[a[idx]] = idx;↵
if (mx[a[idx]]<0) mx[a[idx]] = idx;↵
res = max(res, mx[a[idx]]-mi[a[idx]]);↵
}     ↵

void addright(int idx){↵
mx[a[idx]] = idx;↵
if (mi[a[idx]]>n) mx[a[idx]] = idx;↵
res = max(res, mx[a[idx]]-mi[a[idx]]);↵
}↵

void snapshot(){↵
previous = res;↵
}↵

void rollback(){↵
while(sz(mem)){↵
pair<int,pi> p = mem.back();↵
if (p.fi==0){↵
mi[p.se.fi] = p.se.se;↵
}else{↵
mx[p.se.fi] = p.se.se;↵
}↵
mem.pop_back();↵
}↵
res = previous;↵
}↵

void init(){↵
for (int i=1;i<=m;++i) mi[i] = INF, mx[i] = -INF;↵
  res = 0;↵
}↵

const int block=320; //Dont forget to set↵

struct Query {↵
    int l, r, idx;↵
inline pair<int, int> toPair() const {↵
return make_pair(l / block, r);↵
}↵
};↵
inline bool operator<(const Query &a, const Query &b) {↵
return a.toPair() < b.toPair();↵
}↵

using vq = vector<Query>;↵


vi mo(vq queries) {↵
    vi answers(queries.size());↵
    sort(queries.begin(), queries.end());↵
    /*↵
     Light Queries↵
    */↵
    for (Query q: queries){↵
     if (q.r-q.l+1<=block){↵
     int ans = 0;↵
     for (int j=q.l;j<=q.r;++j){↵
     leftmost[a[j]] = INF;↵
     }↵
     for (int j=q.l;j<=q.r;++j){↵
     ans = max(ans, j-leftmost[a[j]]);↵
     if(leftmost[a[j]]>=n) leftmost[a[j]] = j;↵
     }↵
     answers[q.idx] = ans;↵
     }↵
    }↵
    /*↵
     Heavy Queries↵
    */↵
    int l, r;↵
    int last_bucket = -1;↵
    for (Query q : queries) {↵
        if (q.r-q.l+1<=block) continue;↵
        int bucket = q.l/block;↵
        if (bucket!=last_bucket){↵
         init();↵
         l = (bucket+1)*block;↵
         if (l>=n) l = n;↵
         r = q.r;↵
         for (int j=l;j<=r;++j){↵
         addright(j);↵
         }↵
        }↵
        last_bucket = bucket;↵
        while(r<q.r){↵
         addright(++r);↵
        }↵
        snapshot();↵
        for (int j=l-1;j>=q.l;--j){↵
         addleft(j);↵
        }↵
        answers[q.idx] = res;↵
        rollback();↵
    }↵
    return answers;↵
}↵

void solve()↵
{↵
cin >> n >> m>>k;↵
for (int i=0;i<n;++i) cin >> a[i];↵
vq q(k);↵
for (int i=0;i<k;++i){↵
int l,r;↵
cin >> l >> r;↵
l--;r--;↵
q[i] = {l,r,i};↵
}↵
vi ans = mo(q);↵
for (int x:ans) cout << x <<"\n";↵
}↵

int main()↵
{↵
    ios_base::sync_with_stdio(0);↵
    cin.tie(0), cout.tie(0);↵
    solve();↵
}↵
~~~~~↵


</spoiler>↵

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en2 English Bungmint 2021-07-05 22:52:32 116 Update1
en1 English Bungmint 2021-07-05 21:42:00 5306 Initial revision (published)