#A. Balanced Lineup queuing (rmq template question)

topic


train of thought

It is recommended to look at the detailed explanation of the rmq problem first.

Obviously, this question is meant to follow you a series of numbers, and give multiple inquiries, asking about the difference between the maximum and minimum values ​​in the interval.

If you go to violent enumeration, it will obviously time out, so use the st algorithm to solve it.

We are going to create two RMQ preprocessing content to deal with the maximum value and the minimum value respectively.

Create a mx[i][j] to represent the maximum value in the interval starting from i and length 2^j, and mn[i][j] to represent the minimum value in the interval starting from i and length 2^j.

According to the idea of ​​doubling, an interval with a length of 2^j can be divided into two subintervals with a length of 2^(j-1), and then the maximum value of the two subintervals can be found. Therefore, during preprocessing, the interval is divided into two parts on average from the middle (it doesn’t matter if there is overlap in the middle, and it does not affect the maximum value), and the number of elements in each part is exactly 2^j-1, that is to say, the state transition equation is :

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] represents 2^i power)

If F[i, j] represents the maximum value of the interval [i, i+2^j-1], and the length of the interval is 2^j, what is the value range of i and j?

If the length of the array is n, and the maximum interval length is 2^r≤n<2^(r+1), then r=⌊log2n⌋, for example, k=3 when n=8, and k=3 when n=10. In the program, r=log2(n), i from 1~i<=r, j from 1~s[i]+j-1<=n (because i represents the length of the interval, and j represents the starting position of the interval)

Preprocessing code:

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]);
    }
}

After preprocessing, it is time to start querying.

If you query the maximum and minimum values ​​of the [l,r] interval, first calculate the k value, which is the same as the previous calculation method, and the interval length is r-l+1.

2^k≤r-l+1<2^(k+1), so k=log2(r-l+1).

If the length of the query interval is greater than or equal to 2^k and less than 2^(k+1), then according to the multiplication idea, the query interval can be divided into two query intervals, and the maximum value of the two intervals can be taken. The two intervals are 2^k numbers backward from l and 2^k numbers forward from r. These two intervals may overlap, but they have no effect on finding the maximum value.

查询代码:

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;
}

Guess you like

Origin blog.csdn.net/weq2011/article/details/128767164