Question HDU-1698 à l'effet:
Étant donné une rangée de n bâtons de cuivre, leur valeur initiale est 1. Maintenant, il y a q opérations, vous pouvez changer les bâtons dans l'intervalle [x, y] en bâtons de cuivre (valeur 1), bâtons d'argent (valeur 2), bâtonnet d'or (valeur de 3). Quelle est la valeur de tous les bâtons obtenus à la fin?
Idées:
- Utilisez l'arborescence des segments de ligne pour enregistrer la valeur totale de chaque intervalle.
- En ce qui concerne la mise à jour de l'intervalle [x, y], nous utilisons le tableau paresseux pour marquer, qui représente la valeur du sous-intervalle du nœud d'intervalle actuel qui doit être modifié (mis à jour uniquement lorsque le sous-intervalle est nécessaire pour la mise à jour et la requête)
- Enfin, utilisez la requête de requête pour chaque question. Si l'intervalle de requête couvre complètement l'intervalle de maintenance, la somme des intervalles est directement renvoyée; si elle n'est pas complètement couverte, le nœud de sous-intervalle du nœud d'intervalle actuel sera utilisé, donc vers le bas la mise à jour est une opération pushdown requise
- Lors de la mise à jour d'un certain intervalle, la même raison, s'il est complètement couvert, mise à jour directement; sinon, le sous-intervalle sera utilisé, et l'opération doit être effectuée après le refoulement.
Le code AC est le suivant:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 5;
int lazy[maxn << 2], ms[maxn << 2];
int n, q, x, y, z, t;
void pushup(int id){
ms[id] = ms[id << 1] + ms[id << 1 | 1];
}
void pushdown(int id, int l, int r){
if(lazy[id]){
lazy[id << 1] = lazy[id << 1 | 1] = lazy[id];
int mid = ( l + r ) >> 1;
ms[id << 1] = (mid - l + 1) * lazy[id];
ms[id << 1 | 1] = (r - mid) * lazy[id];
lazy[id] = 0;
}
}
void update(int id, int l, int r, int x, int y, int v){
if(x <= l && r <= y){
lazy[id] = v;
ms[id] = (r - l + 1) * v;
return;
}
pushdown(id, l, r);
int mid = (l + r) >> 1;
if(x <= mid){
update(id << 1, l, mid, x, y, v);
}
if(y > mid){
update(id << 1 | 1, mid + 1, r, x, y, v);
}
pushup(id);
}
int query(int id, int l, int r, int x, int y){
if(x <= l && r <= y){
return ms[id];
}//如果不包含在内,则需要向下更新
pushdown(id, l, r);
int mid = (l + r) >> 1;
int ans = 0;
if(x <= mid){
ans += query(id << 1, l, mid, x, y);
}
if(y > mid){
ans += query(id << 1 | 1, mid + 1, r, x, y);
}
return ans;
}
int main(){
int T = 1;
scanf("%d",&t);
while(t--){
memset(lazy, 0, sizeof lazy);
scanf("%d%d", &n, &q);
update(1, 1, n, 1, n, 1);//将线段树整个区间置位1,即铜棒
for(int i = 1;i <= q;i++){
scanf("%d%d%d", &x, &y, &z);
update(1, 1, n, x, y, z);
}
printf("Case %d: The total value of the hook is %d.\n", T++, query(1, 1, n, 1, n));
}
return 0;
}
Question Horrible Queries LightOJ-1164 à l'effet:
Étant donné un tableau de longueur n, sa valeur initiale est entièrement égale à 0. Il y a q opérations, dont l'opération 1 xyv représente l'ajout de v à la valeur entre l'intervalle [x, y]; et l'opération 0 xy représente la demande de la valeur de somme dans l'intervalle [x, y] pour compléter la question selon l'entrée et la sortie.
Idées:
- Utilisez une arborescence de segments de ligne pour conserver la valeur de somme dans l'intervalle;
- Utilisez le tableau différé pour marquer la valeur à ajouter au sous-intervalle du nœud d'intervalle actuel. Lorsque la valeur du sous-intervalle doit être utilisée, effectuez une opération de refoulement pour mettre à jour la valeur du nœud de sous-intervalle ;
- Enfin, utilisez la requête de requête pour chaque question. Si l'intervalle de requête couvre complètement l'intervalle de maintenance, la somme des intervalles est directement renvoyée; si elle n'est pas complètement couverte, le nœud de sous-intervalle du nœud d'intervalle actuel sera utilisé, donc vers le bas la mise à jour est une opération pushdown requise
- Lors de la mise à jour d'un certain intervalle, la même raison, s'il est complètement couvert, mise à jour directement; sinon, le sous-intervalle sera utilisé, et l'opération doit être effectuée après le refoulement.
Le code AC est le suivant:
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn = 1e5 + 5;
int lazy[maxn << 2];
ll ms[maxn << 2];
//lazy数组用于存储下标为id的区间结点中,左右儿子区间应该增加的数值 。
//lazy的作用是在有需要的时候,更新当前结点的左右儿子节点的区间和值,当不需要更新的时候就不做多余的更新
void pushup(int id){
ms[id] = ms[id << 1] + ms[id << 1 | 1];
}
void pushdown(int id, int l, int r){
//父节点区间应该加上lazy的值那么子区间也应该加上同样的值,此为下方操作
if(lazy[id]){
lazy[id << 1] += lazy[id]; //让左二子更新其子节点应该增加的值
lazy[id << 1 | 1] += lazy[id];
int mid = (l + r) >> 1;
ms[id << 1] += lazy[id] * (mid - l + 1);//左子树的和递增
ms[id << 1 | 1] += lazy[id] * (r - mid);//右子树的和递增
lazy[id] = 0;//增加之后清空,下一次id这个结点区间就不再增加这个值了。
}
}
ll query(int id, int l, int r, int x, int y){
if(x <= l && r <= y){
return ms[id];
}
pushdown(id, l, r);
//所查询的区间不完全覆盖所维护的区间,所以将所维护的区间向下推,使得当前结点的子节点区间按照因该加上的值更新。
int mid = (l + r) >> 1;
ll ans = 0;
if(x <= mid){
ans += query(id << 1, l, mid, x, y);
}
if(y > mid){
ans += query(id << 1 | 1,mid + 1, r, x, y);
}
return ans;
}
void update(int id, int l, int r, int x, int y, int v){
if(x <= l && r <= y){
ms[id] += (r - l + 1) * v;
lazy[id] += v;
}else{
pushdown(id , l, r);//说明当前区间不再查询区间之内,因此要递归到下一层区间结点。
//所以之前存在于lazy数组中还没有更新的值此时需要被更新。
int mid = (l + r) >> 1;
if(x <= mid){
update(id << 1, l, mid, x, y, v);
}
if(y > mid){
update(id << 1 | 1, mid + 1, r, x, y, v);
}
pushup(id);//向上合并。
}
}
int t, n, m, op, x, y, d;
int main() {
int cnt = 1;
scanf("%d", &t);
while (t--) {
memset(lazy, 0, sizeof(lazy));
memset(ms, 0, sizeof(ms));
printf("Case %d:\n", cnt++);
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &op, &x, &y);
x++, y++;
if (op == 0) {
scanf("%d", &d);
update(1, 1, n, x, y, d);
} else {
printf("%lld\n", query(1, 1, n, x, y));
}
}
}
return 0;
}