Moのアルゴリズム
すもももももももものうち
いくつかの半開区間 \([l_i,r_i)\) についての答えを高速に求める \([l,r)\) での答えがわかっているとき、隣接する区間 \([l-1,r),[l+1,r),[l,r-1),[l,r+1)\) についての答えが高速にわかる場合に有効
第一引数に区間全体の長さ \(N\) 、第二引数にクエリの数 \(Q\) 、第三引数にクエリのリスト、そして第四~第七引数にはそれぞれ
- \([l,r)\) の答えから \([l-1,r)\) の答えを求める関数
- \([l,r)\) の答えから \([l+1,r)\) の答えを求める関数
- \([l,r)\) の答えから \([l,r-1)\) の答えを求める関数
- \([l,r)\) の答えから \([l,r+1)\) の答えを求める関数
を与えると、各クエリに対しての答えをvectorで返す
計算量は第四~第七引数で与えた関数の計算量を \(f\) とすると \(Ο(Q \log Q + N \sqrt{Q} f)\)
std::vector<long long> Mo(int N,int Q,std::vector<std::pair<int,int>> X,
std::function<void(int,long long&)> lm, std::function<void(int,long long&)> lp,
std::function<void(int,long long&)> rm, std::function<void(int,long long&)> rp){
// Copyright (c) 2023 0214sh7
// https://github.com/0214sh7/library/
int rQ;
std::map<std::pair<int,int>,std::vector<int>> index;
std::vector<int> block(N,0);
std::vector<long long> Ans(Q);
std::function<bool(std::pair<int,int>, std::pair<int,int>)> comp = [&](std::pair<int,int> A,std::pair<int,int> B){
if(block[A.first]!=block[B.first]){
return (block[A.first]<block[B.first]);
}
if(A.second != B.second){
return (A.second < B.second);
}
return (A.first < B.first);
};
for(int i=0;i<Q;i++){
index[X[i]].push_back(i+1);
}
int q = index.size();
rQ = 1;
while((rQ+1)*(rQ+1)<=q)rQ++;
int now = 0;
for(int i=0;i<rQ-1;i++){
now += ((N+rQ-1-i)/rQ);
block[now]++;
}
for(int i=0;i<N-1;i++){
block[i+1] += block[i];
}
std::vector<std::pair<int,int>> Y(q);
now = 0;
for(auto e:index){
Y[now] = e.first;
now++;
}
std::sort(Y.begin(),Y.end(),[&](auto a, auto b){return comp(a, b);});
int L = 0,R = 0;
long long Result = 0;
for(int i=0;i<q;i++){
while(Y[i].first < L){
lm(L,Result);
L--;
}
while(R < Y[i].second){
rp(R,Result);
R++;
}
while(L < Y[i].first){
lp(L,Result);
L++;
}
while(Y[i].second < R){
rm(R,Result);
R--;
}
for(int k:index[Y[i]]){
Ans[k-1] = Result;
}
}
return Ans;
//lm(i): [i,r)を[i-1,r)にする
//lp(i): [i,r)を[i+1,r)にする
//rm(i): [i,r)を[i,r-1)にする
//rp(i): [i,r)を[i,r+1)にする
}
使用例
\(A = (1,2,3,4,5)\) の \([l,r)\) の和を求める
これは \([l,r)\) の和がわかっていれば \([l-1,r),[l+1,r),[l,r-1),[l,r+1)\) の和が \(O(1)\) でわかる
例えば \([1,3)\) の和が \(A_1 + A_2 = 2 + 3 = 5\) であることがわかっていれば \([1,4)\) の和は \(5 + A_3 = 5 + 4 = 9\) であることがわかる
実行コード
#include <bits/stdc++.h>
std::vector<long long> Mo(int N,int Q,std::vector<std::pair<int,int>> X,
std::function<void(int,long long&)> lm, std::function<void(int,long long&)> lp,
std::function<void(int,long long&)> rm, std::function<void(int,long long&)> rp){/*省略*/}
int main(void){
int N = 5, Q = 15;
std::vector<long long> A = {1,2,3,4,5};
std::vector<std::pair<int,int>> LR = {
{0,1},
{0,2},
{0,3},
{0,4},
{0,5},
{1,2},
{1,3},
{1,4},
{1,5},
{2,3},
{2,4},
{2,5},
{3,4},
{3,5},
{4,5},
};
std::function<void(int,long long&)> lm = [&](int i,long long& res){
res += A[i-1];
};
std::function<void(int,long long&)> lp = [&](int i,long long& res){
res -= A[i];
};
std::function<void(int,long long&)> rm = [&](int i,long long& res){
res -= A[i-1];
};
std::function<void(int,long long&)> rp = [&](int i,long long& res){
res += A[i];
};
std::vector<long long> Ans = Mo(N,Q,LR,lm,lp,rm,rp);
for(int i=0;i<(int)Ans.size();i++){
std::cout << "[" << LR[i].first << "," << LR[i].second << ") " << Ans[i] << std::endl;
}
return 0;
}
出力
[0,1) 1
[0,2) 3
[0,3) 6
[0,4) 10
[0,5) 15
[1,2) 2
[1,3) 5
[1,4) 9
[1,5) 14
[2,3) 3
[2,4) 7
[2,5) 12
[3,4) 4
[3,5) 9
[4,5) 5
更新履歴
日時 | 内容 |
---|---|
2023/06/29 | ライセンスのコメントアウトを変更 |
2023/03/13 | Moのアルゴリズムを追加 |
確認した問題
問題 | 提出 |
---|---|
ABC293-G | 提出 |