間隔の変更フェンウィックツリーをサポートしています
原則
アレイのための\(\) 、および\(\)の差\(C \) 、明らかに\(C [I] =
[I] -a [I-1] \) 、アレイAのそこプレフィックスと
\(\ sum_ {iは1 = } ^ N {a_iを} = C [1] +(C [1] + C [2])+ ...(C [1] + C [2] +。 .. + C [N])\
) さらに:
\(\ sum_ 1 = {I} ^ {a_iを} = N-C + N-C * [2] *(1-N-)+ ... +。[1]。 C [N] *(N-
N + 1)\) 括弧内に展開
\(\ sum_ {i = 1 } ^ nは{a_iを} = C [1] * N + C [2] * N + ... + C [N] * N-(C [
1] *(1-1)+ C [2] *(2-1)+ ... + C [N] *は、(N-1))\) である
(\ \ sum_ ^ {I 1 =} N {a_iを} = N * \ sum_ {I = 1} ^ nは{C [I]} - \ sum_ {I = 1} ^ nは{C [I] *(I-1 )} \)
こうして接頭二つの配列を維持する必要が維持差動プレフィックスと\(C [I] \)と\((1-I)* C [I] \)のメンテナンスに対応し、sum_ {I(\ \ 1} ^ {N- = C [I]} \)、\ (\ sum_ 1} ^ {N-Iは、= {(I-1)* C [I]} \)
の差動アレイ・ツリーの本明細書で使用される場合、二つの配列プレフィックスとメンテナンスは、と命名した\(TR \)と\(TR1 \) 。
実現
最初の2つの基本的な操作のツリーの基本的な配列クリアする範囲クエリと単調な問い合わせを。フェンウィックツリーのメンテナンスとプレフィックスの使用:
- 間隔クエリー\(\クエリ{テキスト}(K)\)、1とkとの接頭辞\(\ sum_ {I = 1 } ^ K {[I]} \)
- 単一の点を変更する(\追加{テキスト}(K、X)\)\、(A [K] + = X \)\
次に、この記事のための支持部変性:樹状配列は、以下を有する
1 間隔変更 \(\追加{テキスト}(L、R、K)\)(L及びRを含む)、操作と等価である(\ \ {ADD1テキスト(L、X)} \) 、\(\ {テキスト} ADD1(R&LT +。1、-X-)\)と\(\テキスト{ADD2}( L、(L-1)* X)\) 、\(\ {テキスト} ADD2(R&LT + 1は、R *( - X))\) (差動特性が定義された)
2.クエリー間隔\(querysum(K)\) 、動作と同等である\(K * \テキスト{Query1を}(K)* K- \テキスト{QUERY2}(K)\)
オペレーティング
基本フェンウィックツリー
達成するために\(O(ログ(N) )\) シングルポイント範囲を、クエリを修正します。
これは、単一の変更、クエリーインターバル、問い合わせのシングルポイントをサポートしています。
int n, m;
ll a[maxn];
ll tr[maxn]; //树状数组1用于维护差分前缀和$\sum_{i=1}^n{c[i]}$
ll tr1[maxn]; //树状数组2用于维护差分前缀和$\sum_{i=1}^n{(i-1)*c[i]}$
int lowbit(int x) { return x & -x; }
void add(ll tr[], int l, ll k) {
for (int i = l; i <= n; i += lowbit(i)) tr[i] = (tr[i] + k) % mod;
}
ll query(ll tr[], int r) {
ll res = 0;
for (int i = r; i; i -= lowbit(i)) res = (res + tr[i]) % mod;
return res;
}
初期化
void init(int nn) {
n = nn + 2;//防止空间越界
for (int i = 0; i <= n; i++) tr[i] = tr1[i] = 0;
}
間隔の変更
//[l,r]区间修改+x
void add(int l, int r, int x) {
add(tr, l, x);
add(tr, r + 1, -x);
add(tr1, l, 1ll * (l - 1) * x);
add(tr1, r + 1, 1ll * r * (-x));
}
インターバル・お問い合わせ
クエリ\(\ sum_ {I}。1 ^ = KA [I] \) 、すなわちクエリ\([K 1] \ ) 接頭辞と以内
//区间查询原数组sum(a[1,k])
ll querysum(int k) {
return (1ll * query(tr, k) * k) - query(tr1, k);
}
間隔の変更
で\([L] ... [R] \) 間隔プラス\(X \) 。
//[l,r]区间修改+x
void add(int l, int r, int x) {
add(tr, l, x);
add(tr, r + 1, -x);
add(tr1, l, 1ll * (l - 1) * x);//防止暴int
add(tr1, r + 1, 1ll * r * (-x));
}
複雑性分析
フェンウィックツリーエッセンス2つのプレフィックスをと差を維持するために、空間の3倍の間隔の長さであり、\(O(N-3 *)\)
時間計算:
次の操作は、\(O(ログ(N) ) \) :
- Xとプラスの間隔
- インターバル・お問い合わせ
- お問い合わせのシングルポイント
- xは単調にプラス
セグメントツリーに比べて空間複雑\(O(4×N) \) より小さい
同時に複雑。
(ほとんどの複雑さをプログラミング彼らは一生懸命ORZです)
統合テンプレート
#define judge
// Author: oceanlvr
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
static int faster_iostream = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
return 0;
}();
const int maxn = 1e6 + 10;
const int mod = 772002 + 233;
int n, m;
ll a[maxn];
ll tr[maxn]; //树状数组1
ll tr1[maxn]; //树状数组2
int lowbit(int x) { return x & -x; }
void add(ll tr[], int l, ll k) {
for (int i = l; i <= n; i += lowbit(i)) tr[i] = (tr[i] + k) % mod;
}
ll query(ll tr[], int r) {
ll res = 0;
for (int i = r; i; i -= lowbit(i)) res = (res + tr[i]) % mod;
return res;
}
//[l,r]区间修改+x
void add(int l, int r, int x) {
add(tr, l, x);
add(tr, r + 1, -x);
add(tr1, l, 1ll * (l - 1) * x);
add(tr1, r + 1, (1ll * r * (-x)%mod+mod)%mod);
}
//区间查询原数组sum(a[1,k])
ll querysum(int k) {
return (((1ll * query(tr, k) * k) % mod - query(tr1, k) % mod) % mod + mod) %
mod;
}
void init(int nn) {
n = nn + 2;
for (int i = 0; i <= n; i++) tr[i] = tr1[i] = 0;
}
/*------------------------------------------------------------------------------*/
//按题目要求区间[l,r]修改 [l+1,r]+d,[l,l]+a0,[r+1,r+1]-前面两个的和
void addad(int l, int r, int a0, int d) {
add(l, l, a0); //单点l上+a0
if (l + 1 <= r) add(l + 1, r, d); //区间[l+1,r] +d
add(r + 1, r + 1,
(-(a0 + (1ll * (r - l) * d)) % mod + mod) % mod); //单点r+1 -前面两个的和
}
//区间查询原数组sum(a[1,k])
int queryad(int k) { return querysum(k); }
int op;
int main() {
#ifndef judge
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i],a[i]%=mod;
init(n);
for (int i = 0; i < m; i++) {
cin >> op;
if (op == 1) {
int x, y;
cin >> x >> y;
//(y+1)y/2
//对[x,x+y-1]加上一个-1
int l = x, r = min(x + y - 1, n);
addad(l, r, y, -1);
} else if (op == 2) {
int x;
cin >> x;
cout << (a[x] + queryad(x)) % mod << endl;
}
}
return 0;
}
参考リンク:フェンウィックツリープラス区間演算シーケンス