Square Root Decomposition or Mo's Algorithm

MO’s algorithm is just an order in which we process the queries. We were given M queries, we will re-order the queries in a particular order and then process them. Clearly, this is an offline algorithm. Each query has L and R, we will call them opening and closing. Let us divide the given input array into Sqrt(N) blocks (virtually) . Each block will be N / Sqrt(N) = Sqrt(N) size. Each opening has to fall in one of these blocks. Each closing has to fall in one of these blocks.
How does the final order look like?
All the queries are first ordered in ascending order of their block number (block number is the block in which its opening falls). Ties are ordered in ascending order of their R value.
For example consider following queries and assume we have 3 blocks each of size 3.
{0, 3} {1, 7} {2, 8} {7, 8} {4, 8} {4, 4} {1, 2}
Let us re-order them based on their block number. (L/Block_size)
{0, 3} {1, 7} {2, 8} {1, 2} {4, 8} {4, 4} {7, 8}
Now let us re-order ties based on their R value.
{1, 2} {0, 3} {1, 7} {2, 8} {4, 4} {4, 8} {7, 8}
Complexity of above algorithm – O(Sqrt(N) * N)
Notes:
  • we cannot use this algorithm when we are forced to stick to given order of queries. 
  • we cannot use this when there are update operations.

Operations(Templates):-


Compare:
bool cmp(node x, node y) {
 if(x.L/BLOCK != y.L/BLOCK) {
  // different blocks, so sort by block.
  return x.L/BLOCK < y.L/BLOCK;
 }
 // same block, so sort by R value
 return x.R < y.R;
}



Conditions:
for each query:
  // currentL should go to L, currentR should go to R

  while currentL &amp;lt; L:
    remove(currentL)
    currentL++

 while currentR &amp;lt; R:
    add(currentR)
    currentR++

  while currentL &amp;gt; L:
    add(currentL)
    currentL--

  while currentR &amp;gt; R:
    remove(currentR)
    currentR--

  output answer


Doubts?

Reference Links:

Lets Jump to some questions :

Question 1:

Given an array of N integers and Q queries each of which asks to find the sum of all numbers Ai, for those Ai which has an odd frequency in subarray L to R

Input Output

5 1
1 2 2 2 1 7
3 6
1 3
1 4
1 5

Solution:
#include <bits/stdc++.h>
using namespace std;

#define N 200005
#define A 200005
int BLOCK;

int cnt[A]={0}, a[N];
long long int sum=0, ans[N];

struct node {
 int L, R, i;
}q[N];

bool cmp(node x, node y) {
 if(x.L/BLOCK != y.L/BLOCK) {
  // different blocks, so sort by block.
  return x.L/BLOCK < y.L/BLOCK;
 }
 // same block, so sort by R value
 return x.R < y.R;
}

void add(int position) {
 cnt[a[position]]++;
 if(cnt[a[position]] %2 == 1) {
  sum+=(a[position]*cnt[a[position]]);
 }
 else{
  if(cnt[a[position]]>=0)
  sum-=(a[position]*max(1,(cnt[a[position]]-1)));
 }
}

void remove(int position) {
 cnt[a[position]]--;
 if(cnt[a[position]]%2 ==1) {
  sum+=(a[position]*cnt[a[position]]);
 }
 else{
  if(cnt[a[position]]>=0)
  sum-=(a[position]*max(1,(cnt[a[position]]-1)));
 }
}

int main() {
 int n;
 scanf("%d", &n);
 BLOCK = sqrt(n);
 for(int i=0; i<n; i++)
  scanf("%d", &a[i]);

 int m;
 scanf("%d", &m);
 for(int i=0; i<m; i++) {
  scanf("%d%d", &q[i].L, &q[i].R);
  q[i].L--; q[i].R--;
  q[i].i = i;
 }

 sort(q, q + m, cmp);

 int currentL = 0, currentR = 0;
 for(int i=0; i<m; i++) {
  
  int L = q[i].L, R = q[i].R;
  //cout<<L<<" "<<R<<endl;
  while(currentL > L) {
   add(currentL-1);
   currentL--;
   //cout<<sum<<" ";
  }
  while(currentR <= R) {
   add(currentR);
   currentR++;
   //cout<<sum<<" ";
  }
  while(currentL < L) {
   remove(currentL);
   currentL++;
   //cout<<sum<<" ";
  }
  
  while(currentR > R+1) {
   remove(currentR-1);
   currentR--;
   //cout<<sum<<" ";
  }
  ans[q[i].i] = sum;
  //cout<<endl;
 }

 for(int i=0; i<m; i++)
  printf("%lld\n", ans[i]);
}


Question 2: Link To Question
N strings numbered 1 to N. The number of times the string S appears in the range [l,r]
 Input                       Output
3                     1
abc                   2
def                   0
abc
3
1 2 abc
1 3 abc
1 2 hgj
Solution:
#include <bits/stdc++.h>
using namespace std;

#define N 100005
#define A 100005
int BLOCK;

vector <string> a(N);
unordered_map<string,int> mymap;
int sum=0, ans[N];

struct node {
 int L, R, i;
 string s;
}q[N];

bool cmp(node x, node y) {
 if(x.L/BLOCK != y.L/BLOCK) {
  // different blocks, so sort by block.
  return x.L/BLOCK < y.L/BLOCK;
 }
 // same block, so sort by R value
 return x.R < y.R;
}

void add(int position) {
    mymap[a[position]]+=1;
}

void remove(int position) {
  mymap[a[position]]-=1;
}

int main() {
 int n;
 scanf("%d", &n);
 BLOCK = sqrt(n);
 for(int i=0; i<n; i++)
 {
     string s1;
     cin>>a[i];
     //cout<<s1<<endl;
     //a.push_back(s1);
 }

 int m;
 scanf("%d", &m);
 for(int i=0; i<m; i++) {
  cin>>q[i].L>>q[i].R>>q[i].s;
  q[i].L--; q[i].R--;
  q[i].i = i;
 }

 sort(q, q + m, cmp);

 int currentL = 0, currentR = 0;
 for(int i=0; i<m; i++) {
  
  int L = q[i].L, R = q[i].R;
  string S=q[i].s;
  //cout<<L<<" "<<R<<endl;
  while(currentL < L) {
   remove(currentL);
   currentL++;
   //cout<<sum<<" ";
  }
  while(currentL > L) {
   add(currentL-1);
   currentL--;
   //cout<<sum<<" ";
  }
  while(currentR <= R) {
   add(currentR);
   currentR++;
   //cout<<sum<<" ";
  }
  while(currentR > R+1) {
   remove(currentR-1);
   currentR--;
   //cout<<sum<<" ";
  }
  ans[q[i].i] = mymap[S];
  //cout<<endl;
 }

 for(int i=0; i<m; i++)
  printf("%d\n", ans[i]);
}
Question 3: Link To Question (Similar to Mo) 
Each query consists of two integers  and .
For each query, you have to consider all the elements of array greater than or equal to , in original order of occurrence in array  and then find the  element from the selected elements. 
Solution:
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

struct node{
    int L,K,i;
}q[100005];

bool cmp(struct node a,struct node b){
    if(a.L != b.L)
            return a.L< b.L;
    return a.K<b.K;
}

int ans[100005];
int main() {
    ios_base::sync_with_stdio(0);cin.tie(0);
    int n,m;
    cin>>n>>m;
    int a[n];
    for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=0;i<m;i++){
        cin>>q[i].L>>q[i].K;
        q[i].i=i;
    }
    sort(q,q+m,cmp);
    int lstL = -1,pos=0,lstK=0,kdiff;
    for(int i=0;i<m;i++){
        int L=q[i].L,K=q[i].K;
        //cout<<L<<" "<<K<<endl;
        if(L!=lstL){
            // pos=0;
            // while(pos<n && a[pos]!=L)
            //     pos++;
            lstL=L;
            lstK=0;
            pos=0;
        }
        //cout<<pos<<" "<<a[pos]<<endl;
        kdiff=K-lstK;
        //cout<<"kdiff "<<kdiff<<endl;
        while(kdiff>0){
            if(a[pos++]>=L){
                kdiff--;
            }
            //pos++;
        }
        //cout<<pos<<" "<<a[pos-1]<<endl;
        lstK=K;
        ans[q[i].i]=a[pos-1];
    }
    for(int i=0;i<m;i++){
        cout<<ans[i]<<endl;
    }
    return 0;
}
Practice Problems:










Comments

Popular posts from this blog

Lazy Propagation - Too lazy to update all at a time

Combinatorial Game Theory

XOR Problems using Trie