tema
linha de raciocínio
Recomenda-se examinar primeiro a explicação detalhada do problema rmq .
Obviamente, esta questão tem como objetivo acompanhar uma série de números, e dar múltiplas consultas, perguntando sobre a diferença entre os valores máximo e mínimo no intervalo.
Se você for para a enumeração violenta, obviamente o tempo expirará, então use o algoritmo st para resolvê-lo.
Criaremos dois conteúdos de pré-processamento RMQ para lidar com o valor máximo e o valor mínimo, respectivamente.
Crie um mx[i][j] para representar o valor máximo no intervalo começando em i e comprimento 2^j, e mn[i][j] para representar o valor mínimo no intervalo começando em i e comprimento 2^j .
De acordo com a ideia de duplicação, um intervalo com comprimento de 2^j pode ser dividido em dois subintervalos com comprimento de 2^(j-1), e então o valor máximo dos dois subintervalos pode ser encontrado. Portanto, durante o pré-processamento, o intervalo é dividido em duas partes uniformemente a partir do meio (não importa se há sobreposição no meio, e isso não afeta o valor máximo), e o número de elementos em cada parte é exatamente 2 ^ j-1, ou seja, a equação de transição de estado é:
mx[j][i] = max(mx[j][i - 1],mx[j + s[i - 1]][i - 1])
mn[j][i] = min(mn[j][i - 1],mn[j + s[i - 1]][i - 1])
(s[i] representa 2^i potência)
Se F[i, j] representa o valor máximo do intervalo [i, i+2^j-1], e o comprimento do intervalo é 2^j, qual é o intervalo de valores de i e j?
Se o comprimento da matriz for n e o comprimento máximo do intervalo for 2^r≤n<2^(r+1), então r=⌊log2n⌋, por exemplo, k=3 quando n=8, e k= 3 quando n=10. No programa, r=log2(n), i from 1~i<=r, j from 1~s[i]+j-1<=n (porque i representa a duração do intervalo e j representa o início posição do intervalo)
Código de pré-processamento:
void pre()
{
int r = log2(n);
for(int i = 1; i <= r; i++)
for(int j = 1; s[i] + j - 1 <= n; j++)
{
mx[j][i] = max(mx[j][i - 1],mx[j + s[i - 1]][i - 1]);
mn[j][i] = min(mn[j][i - 1],mn[j + s[i - 1]][i - 1]);
}
}
Após o pré-processamento, é hora de iniciar a consulta.
Se você consultar os valores máximo e mínimo do intervalo [l,r], primeiro calcule o valor k, que é igual ao método de cálculo anterior, e a duração do intervalo é rl+1.
2^k≤r-l+1<2^(k+1), então k=log2(rl+1).
Se o comprimento do intervalo de consulta for maior ou igual a 2^k e menor que 2^(k+1), então, de acordo com a ideia de multiplicação, o intervalo de consulta pode ser dividido em dois intervalos de consulta, e o valor máximo de os dois intervalos podem ser tomados. Os dois intervalos são 2 ^ k números para trás a partir de L e 2 ^ k números para frente a partir de R. Esses dois intervalos podem se sobrepor, mas não têm efeito na localização do valor máximo.
查询代码:
int f(int x,int y)
{
int r = log2(y - x + 1);
int t1 = max(mx[x][r],mx[y - s[r] + 1][r]);
int t2 = min(mn[x][r],mn[y - s[r] + 1][r]);
return t1 - t2;
}
整体代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,q,a[1000001],mx[1000001][20],mn[1000001][20],s[1000001],x,y;
void pre()
{
int r = log2(n);
for(int i = 1; i <= r; i++)
for(int j = 1; s[i] + j - 1 <= n; j++)
{
mx[j][i] = max(mx[j][i - 1],mx[j + s[i - 1]][i - 1]);
mn[j][i] = min(mn[j][i - 1],mn[j + s[i - 1]][i - 1]);
}
}
int f(int x,int y)
{
int r = log2(y - x + 1);
int t1 = max(mx[x][r],mx[y - s[r] + 1][r]);//最大
int t2 = min(mn[x][r],mn[y - s[r] + 1][r]);//最小
return t1 - t2;//差
}
signed main()
{
s[0] = 1;
for(int i = 1; i <= 20; i++) s[i] = s[i - 1] * 2;//s[i]代表2^i次方
scanf("%lld%lld",&n,&q);
for(int i = 1; i <= n; i++)
{
scanf("%lld",&t);
mx[i][0] = mn[i][0] = t;//初始化,从第i个位置向后延2^0位的最大值和最小值都是输入的第i个位置上的数
}
pre();//预处理
for(int i = 1; i <= q; i++)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",f(x,y));//查询
}
return 0;
}