Intervalo em árvore do segmento de linha Valor máximo consulta / atualização de intervalo Implementação simples

Definição de árvore de segmento de linha

Segmento de linha = matriz unidimensional
Como mostrado na figura: de acordo com o índice, uma matriz unidimensional é dividida em duas metades, cada nó da árvore do segmento de linha representa um subintervalo na matriz original e o nó da folha representa um comprimento de 1. Intervalo
Insira a descrição da imagem aqui

O que uma árvore de linhas pode fazer

Principalmente para resolver o problema do intervalo, como consultar o valor máximo de um intervalo, adicionar ou subtrair um número a todos os elementos de um intervalo ou encontrar a soma de um determinado intervalo.Para intervalos estáticos, temos métodos melhores, como [ prefix Soma e matriz de diferenças ], [ ST do RMQ query static ]

Mas esses métodos têm desvantagens: e se nossa matriz for atualizada dinamicamente? Ou seja, consulta durante a atualização.

  • árvore segmento pode atualizar dinamicamente o intervalo, enquanto consulta mais rápida refere-se a O (log (n))

Estrutura de árvore de linha

Com base em muitos recursos acima, os nós de uma árvore de segmentos de linha precisam ter:

  • O que está atualmente representado
  • Filhos esquerdo e direito
  • O valor armazenado (como o valor máximo do intervalo atualmente representado)

Se você usar uma matriz (ou seja, uma árvore binária completa) para armazenar, poderá ser necessário adicionar

  • Subscrito na matriz

Você pode escrever rapidamente a definição da estrutura

typedef struct node
{
	int l, r, i, val;
}node;

Na verdade, ele não precisa ser tão problemático, basta salvar [val], ou seja, o valor armazenado no nó da árvore de linhas pode ser armazenado em uma matriz unidimensional

Usamos uma matriz para simular uma árvore binária completa para armazenar nós, mas devido à natureza bipartida, podemos enviar diretamente o sub-intervalo representado pelo nó atual, seja ele pesquisando ou atualizando , para que o armazenamento precise armazenar apenas o valor do nó Ponto, porque se estiver cheio, o número real de nós é 1 + 2 + 4 + 8 + ... + n/2 + n, ou seja , árvore binária completa

int tree[maxn*4];

Criação de árvore de segmentos

Use uma matriz para salvar os nós. Se você estiver familiarizado com a árvore binária, será rápido
Insira a descrição da imagem aqui
. O índice do nó atual na matriz é i, então o índice filho esquerdo 2*ie o direito filho2*i+1

Também criado de forma recursiva, a matriz original com conhecida a[]conservação, trees[]uma matriz de valores de nó, armazenados a faixa de valor do nó que é o representante do maior elemento de

Definir os intervalos atuais estão a ser criado [l, r], array árvore isubscrito representa esse intervalo

  • Se l = r, tree[i] = a[l]encerre a recursão
  • Caso contrário, recursione as subárvores esquerda e direita, ou seja, crie duas metades de subintervalos e seus subscritos na árvore :, 2*i 与 2*i+1atualize o subscrito i após o término da recursão, ou seja,tree[i] = max(tree[2*i], tree[2*i+1])
/*
i : 当前线段树节点在tree数组中的下标
l : 当前线段树节点代表的区间的左端点
r : 当前线段树节点代表的区间的右端点
*/
void creat(int i, int l, int r)
{
	if(l==r) {tree[i]=a[l]; return;}
	creat(2*i, l, (l+r)/2), creat(2*i+1, (l+r)/2+1, r);
	tree[i] = max(tree[2*i], tree[2*i+1]);
}

Consulta de segmento de linha

Um intervalo de consulta, porque temos alguns valores subdivisão dois intervalos (ou seja, a presença de árvores na matriz), enquanto para encontrar um determinado intervalo, de modo a que estas secções são ligados entre si para olhar para o intervalo igual para [] , e, em seguida, mesclar as respostas a estes intervalos podem ser , Ainda use a consulta recursiva, e sempre podemos encontrar alguns intervalos adequados, porque o intervalo mínimo é o comprimento de 1; se você não conseguir, pode apenas destacá-lo.

Suponha que o intervalo de consulta seja Q. Só precisamos encontrar todos os subintervalos de Q e depois mesclar as respostas.Este processo usa recursão.

  • Se o intervalo representado pelo nó recursivo atual não se cruzar com Q , pare a recursão e retorne o valor mínimo
  • Se o intervalo representado pelo nó recursivo atual for um sub-intervalo de Q , pare a recursão e retorne o valor armazenado no intervalo atual
  • Caso contrário, o intervalo representado pelo nó recursivo atual é dividido pela metade e os subintervalos esquerdo e direito são recursivos
/*
ql : 要查询的区间左端点
qr :要查询的区间右端点
i  : 当前线段树节点在tree数组中的下标
l  : 当前线段树节点代表的区间左端点
r  : 当前线段树节点代表的区间右端点
*/
int query(int ql, int qr, int i, int l, int r)
{
	if(qr<l || r<ql) return INT_MIN;
	if(ql<=l && r<=qr) return tree[i];
	return max(query(ql, qr, 2*i, l, (l+r)/2), 
			   query(ql, qr, 2*i+1, (l+r)/2+1, r));
}

Atualização do intervalo da árvore do segmento de linha

A atualização de todos os números em um intervalo , a complexidade simples nlog (n), é um pouco lenta.Introduzimos um mecanismo de cache que armazena em cache todos os números no intervalo atual.Quando consultamos, podemos usar esse número Para fazer adição e subtração

int buffer[maxn*4];

Mas o problema é que, se você executar várias operações de atualização, e esses intervalos de atualização forem diferentes, e mesmo houver um cruzamento, a situação se tornará problemática

Para atualizar um intervalo Q, se se sabe que o número v a ser atualizado nesse intervalo é armazenado em cache, o resultado do valor máximo desse intervalo será adicionado a v, e nós modificaremos diretamente o valor armazenado na matriz em árvore, ou seja tree[i]+=v, para que possamos imediatamente Atualize para o valor máximo desse intervalo e jogue o problema para os dois subintervalos da próxima camada , ou seja, o cache dos subintervalos esquerdo e direito foi aumentado!

A vantagem disso é que precisamos de um pequeno número de operações para concluir a modificação de um intervalo, e o restante das operações são deixadas para a consulta para "fazê-lo"

Vale ressaltar que

  • Ao atualizar, somente quando o intervalo atual é um subintervalo de Q , o valor a ser adicionado pode ser armazenado em cache e, em seguida, a pergunta é lançada no subintervalo da próxima camada, e a resposta dessa camada é atualizada e retornada.
  • Se você precisar recuar para a próxima camada ao atualizar, não se esqueça de lançar o cache dessa camada primeiro e depois operar
  • Após a atualização recursiva da chamada, lembre-se de atualizar o valor dessa camada antes de retornar
  • Antes de consultar, lembre-se sempre de limpar o cache do intervalo atual, ou seja, jogue o problema para a próxima camada
/*
清空在tree数组中i下标代表的区间的缓存,向下一层抛出缓存
*/
void clear_buf(int i)
{
	// 更新当前节点存储的值 
	tree[i] += buffer[i];
	// 将缓存传递到下一层的节点 
	buffer[2*i] += buffer[i];
	buffer[2*i+1] += buffer[i];
	// 清空当前节点缓存 
	buffer[i] = 0;
}

/*
ul  : 要更新的区间左端点
ur  : 要更新的区间右端点
i   : 当前区间的值存储在tree数组i下标
l   : 当前区间左端点
r   : 当前区间右端点
val : 要更新的值
*/
void update(int ul, int ur, int i, int l, int r, int val)
{
	if(ur<l || r<ul) return;
	if(ul<=l && r<=ur)
	{
		buffer[i] += val;
		clear_buf(i);
		return;
	}
	clear_buf(i);
	update(ul, ur, 2*i, l, (l+r)/2, val);
	update(ul, ur, 2*i+1, (l+r)/2+1, r, val);
	tree[i] = max(tree[2*i], tree[2*i+1]);
}

Ao mesmo tempo, a função de consulta também deve se lembrar de descartar o cache

int query(int ql, int qr, int i, int l, int r)
{
	if(qr<l || r<ql) return INT_MIN;
	clear_buf(i);
	if(ql<=l && r<=qr) return tree[i];
	return max(query(ql, qr, 2*i, l, (l+r)/2), 
			   query(ql, qr, 2*i+1, (l+r)/2+1, r));
}

Código

#include <bits/stdc++.h>

using namespace std;

#define maxn 1000

int tree[maxn*4];
int buffer[maxn*4];
int a[maxn], n;

void creat(int i, int l, int r)
{
	if(l==r) {tree[i]=a[l]; return;}
	creat(2*i, l, (l+r)/2), creat(2*i+1, (l+r)/2+1, r);
	tree[i] = max(tree[2*i], tree[2*i+1]);
}

void clear_buf(int i)
{
	// 更新当前节点存储的值 
	tree[i] += buffer[i];
	// 将缓存传递到下一层的节点 
	buffer[2*i] += buffer[i];
	buffer[2*i+1] += buffer[i];
	// 清空当前节点缓存 
	buffer[i] = 0;
}

int query(int ql, int qr, int i, int l, int r)
{
	if(qr<l || r<ql) return INT_MIN;
	clear_buf(i);
	if(ql<=l && r<=qr) return tree[i];
	return max(query(ql, qr, 2*i, l, (l+r)/2), 
			   query(ql, qr, 2*i+1, (l+r)/2+1, r));
}

void update(int ul, int ur, int i, int l, int r, int val)
{
	if(ur<l || r<ul) return;
	if(ul<=l && r<=ur)
	{
		buffer[i] += val;
		clear_buf(i);
		return;
	}
	clear_buf(i);
	update(ul, ur, 2*i, l, (l+r)/2, val);
	update(ul, ur, 2*i+1, (l+r)/2+1, r, val);
	tree[i] = max(tree[2*i], tree[2*i+1]);
}

int main()
{	
	memset(buffer, 0, sizeof(buffer)); 
	cin>>n;
	for(int i=1; i<=n; i++) cin>>a[i];
	creat(1, 1, n);
	
	update(1, 4, 1, 1, n, 10);
	update(3, 4, 1, 1, n, 10);
	
	for(int l=1; l<=n; l++)
		for(int r=l; r<=n; r++)
			cout<<l<<" "<<r<<" "<<query(l, r, 1, 1, n)<<endl;
	
	return 0;
}

/*
8
1 2 3 4 5 6 7 8
*/
8
1 2 3 4 5 6 7 8
[1, 1] 11
[1, 2] 12
[1, 3] 23
[1, 4] 24
[1, 5] 24
[1, 6] 24
[1, 7] 24
[1, 8] 24
[2, 2] 12
[2, 3] 23
[2, 4] 24
[2, 5] 24
[2, 6] 24
[2, 7] 24
[2, 8] 24
[3, 3] 23
[3, 4] 24
[3, 5] 24
[3, 6] 24
[3, 7] 24
[3, 8] 24
[4, 4] 24
[4, 5] 24
[4, 6] 24
[4, 7] 24
[4, 8] 24
[5, 5] 5
[5, 6] 6
[5, 7] 7
[5, 8] 8
[6, 6] 6
[6, 7] 7
[6, 8] 8
[7, 7] 7
[7, 8] 8
[8, 8] 8
Publicado 262 artigos originais · ganhou 11 · 10 mil visualizações

Acho que você gosta

Origin blog.csdn.net/weixin_44176696/article/details/105187658
Recomendado
Clasificación