

問題の声明
配列A [i]が与えられ、次の形式のリクエストが与えられます:
- セグメントの金額を計算[L; R](後で、関数min、max、gcdなどを同様に計算できることを理解します。
- 要素A [i]に追加、デルタ
素朴な実装
つまり、部分量の配列を計算できます。
for(int j = 0; j < i; j++) B[j] += A[i];
そして、量の要求で[L; R]、B [R] -B [L-1]を返します 
分解棟
主なアイデアは
sqrtSums []配列の構築について説明します。
int len = (int)sqrt(n) + 1; // "sqrt-" int sqrtSums[MAXN], st = 0; // for(int i = 0; i < n; i++) sqrtSums[i / len] += A[i];
リクエストの説明
金額リクエスト(RSQ)
クエリの合計[L; R]、それに対する「迅速な」応答のために、すでに利用可能な情報を最大限に使用することが必要です。 すでに述べたように、サム[L; R] =合計[0、R]-合計[0; L-1]。 リクエストの処理方法Sum [0; R]。 最初のいくつかのセグメント
([0; len-1], [len; 2*len-1], ...)
がクエリ[0;に完全に含まれることに注意してください。 R]、つまり
sqrtSums[]
情報を使用できることを意味し
sqrtSums[]
(
[k*len; k*len + delta]
[k*len; k*len + delta]
(delta <len、そうでない場合は別のセグメント
[k*len; (k+1)*len-1]
を含めることができるため)、手動で計算できます(delta <len、漸近的に、
int getPartSum(int r) { int it = 0, res = 0; while((it+1) * len -1 <= r) res += sqrtSums[it++]; // , for(int i = it*len; i <=r; i++) res += A[i]; // "" return res; } int getSum(int l, int r) { if(l == 0) return getPartSum(r); else return getPartSum(r) - getPartSum(l-1); }
アイテム変更リクエスト
多くの構造では、このクエリを実装するには、ツリーをたどるか、ハッシュ関数などを計算する必要があります...ただし、ここでは2つの値のみを変更する必要があります(各要素は1つの「sqrtセグメント」に含まれます)
int incElement(int index, int delta) { A[index] += delta; sqrtSums[index/len] += delta; }
範囲最小/最大/ Gcdクエリ
RMQクエリ(範囲最小/最大クエリ)に応答するには、合計とほぼ同じものが必要です。 ただし、「愚かな」アイテムを更新するには、
最小値を更新するコード:
int decElement(int index, unsigned int delta) //delta - { A[index] -= delta; sqrtSums[index/len] = min(sqrtSums[index/len], A[index]); }
同様に、最大値を更新するには:
int incElement(int index, unsigned int delta) //delta - { A[index] += delta; sqrtSums[index/len] = max(sqrtSums[index/len], A[index]); }
RMQ問題では、タスクは[0; R]-[0; L-1]、つまり LからRまでの最小値/最大値を計算する必要があります(再度カウントしないで、[L; R]に完全に含まれる「sqrtセグメント」、漸近性が変化しない理由を考えてください。
GCD(Greatest Common Divisor)を計算するために、最小/最大で回した「チップ」を使用することはできません。 したがって、両方の操作は
GCDのコード(最小および最大の場合、getGCD関数はmin / maxに置き換えられます):
int getGCD(int a, int b) { while(a && b) { if(a < b) b %= a; else a %= b; } return a + b; } int gcdRQ(int l, int r) { int cur_gcd = A[l++]; for(int i = l; i <= r;) if (i % len == 0 && i + len - 1 <= r) { cur_gcd = getGCD(cur_gcd, b[i / len]); i += len; // "sqrt-" } else cur_gcd = getGCD(cur_gcd, A[i++]); }
ソース
つまり 、 このサイトを使用しました。
ハリコフのウィンターコンピュータースクールからの講義がこの記事に影響を与えたので、私はサイトを放り投げます。ビデオ講義を含む今年のコレクションがもうすぐ出るでしょう。
今のところ、それが明確であったことを願っています。