Загрузка [MathJax]/jax/output/HTML-CSS/fonts/TeX/fontdata.js

Назад

Подразделы

Другие разделы

Дата и время

08/04/2025 21:30:28

Авторизация

Имя:
Пароль:
Зарегистрироваться
Восстановить пароль
 

printАлгоритм Мо

Алгоритм Мо применяется для решения следующей задачи. Задан массив из N элементов, для которого выполняется M запросов на получение некоторой информации об элементах массива с lj-го по rj-й.
Ограничения алгоритма:
  • массив не изменяется;
  • информация может быть получена за O(N) последовательной обработкой элементов массива.
Алгоритм Мо позволяет обработать M запросов за O(NN+MN+Mlog  действий. Он является комбинированным алгоритмом, основанным на sqrt-декомпозиции, методе двух указателей и предварительной сортировке запросов.
Запросы делятся на группы, соответствующие интервалу, в который попадает левая граница запроса. Каждая группа соответствует интервалу из sqrt{N} элементов. Вначале запросы сортируются по номеру группы, в которую попадает левая граница, а при совпадении номеров групп – по правой границе:
int a[N];
struct query { int l,r,i; } q[M]; 
struct Mo_cmp { 
  int k; 
  Mo_cmp(int k):k(k){}
  bool operator() (const query &a, const query &b) 
  { return (a.l/k!=b.l/k) ? (a.l<b.l) : (a.r<b.r); 
  } 
}; 
...
cin>>n>>m; 
for(int i=0;i<n;++i) cin>>a[i];
for(int i=0;i<m;++i) 
{ cin>>q[i].l>>q[i].r; 
  --q[i].l; --q[i].r; 
  q[i].i=i; 
} 
sort(q,q+m,Mo_cmp(sqrt(n)));
На следующем этапе для обработки упорядоченной последовательности запросов применяется метод двух указателей. Для каждого запроса производится перемещение концов интервала, пока они не совпадут с заданным и запоминание информации в массиве ответов. После обработки запросов полученные ответы выводятся. Пример программы для подсчета результатов по формуле sum_{forall\ v\ in\ a_l…a_r}\ "count"^2(v)*v, где "count"(v) – количество v среди a_l…a_r.
int answer[M]; 
struct state { 
  int l,r; 
  long long sum;
  int c[MAX]; 
  state():l(0),r(-1), sum(0) { fill(c,c+MAX,0); } 
  void change(int v, int d) { 
    sum-=(long long)c[v]*c[v]*v
    c[v]+=d;
    sum+=(long long)c[v]*c[v]*v;
  } 
  void addLeft() { change(a[--l],1); } 
  void addRight() { change(a[++r],1); } 
  void delLeft() { change(a[l++],-1); } 
  void delRight() { change(a[r--],-1); } 
  int answer()const { return sum; } 
} s;
...
state s; 
for(int i=0;i<m;++i) 
{ query qi=q[i]; 
  while(s.l>qi.l) s.addLeft(); 
  while(s.r<qi.r) s.addRight(); 
  while(s.l<qi.l) s.delLeft(); 
  while(s.r>qi.r) s.delRight(); 
  answer[qi.i]=s.answer(); 
} 
for(int i=0;i<m;++i) cout<<answer[i]<<"\n";
Анализ алгоритма. На сортировку уходит O(M\ log\ M) действий. На перемещение левого конца интервала будет использовано не более O(M\ sqrt{N}+N) действий. На перемещение правого конца интервала для каждой группы будет использовано не более O(N) действий, всего групп sqrt{N}, поэтому для всего набора запросов необходимо не более O(N\ sqrt{N}). Итого нужно O(N*sqrt{N}+M*sqrt{N}+M*log\ M) действий.
loading