Par exemple:
int get(int i) {
int res = 0;
while (i) {
res = (res + tree[i]) % MOD;
i -= ( (i) & (-i) );
}
return res;
}
Une fonction de mise à jour de l'arborescence:
void update(int i, int val) {
while (i <= m) {
tree[i] = (tree[i] + val) % MOD;
i += ( (i) & (-i) );
}
}
Pouvez-vous expliquer ce qu'ils font dans le code en utilisant ( (i) & (-i) )
?
Ces deux fonctions sont une implémentation modifiée d'une structure de données arbre d'index binaire (arbre de Fenwick) .
Voici deux images pour compléter la réponse de MikeCAT montrant comment i la variable se met à jour pour différentes valeurs.
La fonction "get":
Pour supposer que la valeur maximale de l'entrée dans la fonction est 15 pour la simplicité de la représentation.
Un nœud avec le numéro t dessus représente l'arbre [t] dans l'arborescence.
Si vous appelez obtenez la fonction pour i le retour la valeur est la somme de arbre [i] plus la somme de tout arbre tableau les éléments dont leur index dans le tableau est un parent de i dans l'image, sauf zéro.
Voici quelques exemples:
get(15) = tree[15] + tree[14] + tree[12] + tree[8]
get(14) = tree[14] + tree[12] + tree[8]
get(13) = tree[13] + tree[12] + tree[8]
get(12) = tree[12] + tree[8]
get(11) = tree[11] + tree[10] + tree[8]
get(10) = tree[10] + tree[8]
get(9) = tree[9] + tree[8]
get(8) = tree[8]
get(7) = tree[7] + tree[6] + tree[4]
get(6) = tree[6] + tree[4]
get(5) = tree[5] + tree[4]
get(4) = tree[4]
get(3) = tree[3] + tree[2]
get(2) = tree[2]
Les nombres sur les étiquettes des nœuds dans l'image ci-dessus ont la propriété que le parent de chaque nœud est cette étiquette de nœud moins la moins significative 1 (très bien expliqué sur la réponse @MikeCAT) La fonction "mise à jour":
Pour simplifier l'image, supposons que la longueur maximale de l'arborescence est de 16.
La fonction de mise à jour est un peu plus délicate.
Il ajoute val à arbre [i] et tous les éléments d'arbre dont leur index est parent du nœud avec l'étiquette i sur la photo.
update(16, val) --> tree[16] += val;
update(15, val) --> tree[15] += val, tree[16] += val;
update(14, val) --> tree[14] += val, tree[16] += val;
update(13, val) --> tree[13] += val, tree[14] += val; tree[16] += val;
update(12, val) --> tree[12] += val, tree[16] += val;
update(11, val) --> tree[11] += val, tree[12] += val, tree[16] += val;
update(10, val) --> tree[10] += val, tree[12] += val, tree[16] += val;
update(9, val) --> tree[9] += val, tree[10] += val, tree[12] += val, tree[16] += val;
update(8, val) --> tree[8] += val, tree[16] += val;
update(7, val) --> tree[7] += val, tree[8] += val, tree[16] += val;
update(6, val) --> tree[6] += val, tree[8] += val, tree[16] += val;
update(5, val) --> tree[5] += val, tree[6] += val, tree[8] += val, tree[16] += val;
update(4, val) --> tree[4] += val, tree[8] += val, tree[16] += val;
update(3, val) --> tree[3] += val, tree[4] += val, tree[8] += val, tree[16] += val;
update(2, val) --> tree[2] += val, tree[4] += val, tree[8] += val, tree[16] += val;
update(1, val) --> tree[1] += val, tree[2] += val, tree[4] += val, tree[8] += val, tree[16] += val;
Permettez-moi de supposer que la valeur négative est représentée en utilisant le complément à deux. Dans ce cas, -i
peut être calculé comme (~i)+1
(retournez les bits, puis ajoutez 1).
Par exemple, permettez-moi de considérer i = 44
. Ensuite, en binaire,
i = 0000 0000 0000 0000 0000 0000 0010 1100
~i = 1111 1111 1111 1111 1111 1111 1101 0011
-i = (~i)+1 = 1111 1111 1111 1111 1111 1111 1101 0100
(i) & (-i) = 0000 0000 0000 0000 0000 0000 0000 0100
Comme vous le voyez, le moindre bit égal à 1 peut être calculé à l'aide de (i) & (-i)
.
Au cas où quelqu'un voudrait également une preuve plus générale,
Supposons que x
a le format a10k (ce qui signifie ici, une chaîne de bits a, suivie d'un 1, suivi de k zéros).
-x
est (par définition) la même chose que ~x + 1
, donc
Il ne nous reste donc que le 1 le plus à droite que nous supposions exister.
L'hypothèse concernant la forme de x
laisse de côté le cas où x = 0
, auquel cas le résultat est évidemment toujours nul.