Maksimum#4473

Maksimum (Həll təhlili)

Məsələ aralıq maksimum sorğularına cavab verməyi tələb edir. Massivin elementləri dəyişmir, ona görə də statik data strukturu uyğundur. Sorğuların sayı 5 \cdot 10^5-ə çatır, massivin uzunluğu isə 10^5-dir. Hər seqmentin maksimumunu birbaşa hesablamaq 5 \cdot 10^{10}-a qədər əməliyyat tələb edərdi ki, bu da çox yavaşdır.

Biz RMQ məsələsi üçün sparse table-dan istifadə edirik, lakin minimum əvəzinə maksimumu axtarırıq (Range Maximum Query). Maksimum, minimum kimi, hər hansı elementin iki dəfə sayılması halında dəyişmir. Ona görə də sorğu seqmenti 2^k uzunluqlu iki üst-üstə düşən seqmentlə örtülə bilər, və onların maksimumlarının böyüyü seçilir.

Cədvəlin bir xanası ya maksimum elementin indeksini, ya da maksimumun özünü saxlaya bilər. Məsələ yalnız dəyəri çap etməyi tələb edir, ona görə də yalnız maksimumu saxlamaq kifayətdir. İndeks variantı maksimumun mövqeyinin məlum olması lazım olduqda lazımdır. Hər iki tətbiq aşağıda verilmişdir.

Məsələnin şərtinə görə, 1 \le l, r \le n, və l \le r olması təmin edilmir. Ona görə də sorğunu icra etməzdən əvvəl sərhədləri lazım olduqda dəyişirik.

Mürəkkəblik. Sparse table-ın qurulması O(n \log n) vaxt alır, hər sorğu isə vaxt alır. Ümumi mürəkkəblik O(n \log n + q)-dır.

Misal

Misaldakı massivi nəzərdən keçirək: (5, 1, 2, 8, 7, 6, 10, 7, 5, 6). mas[i][j]-nin [i; i + 2^j - 1] seqmentindəki maksimum olduğu cədvəli aşağıda göstərilmişdir. Tire işarəsi seqmentin massivdən kənara çıxdığını bildirir.

\boldsymbol{j \setminus i}

—

—

—

—

—

—

—

—

—

—

—

Məsələn, mas[4][2] = \max(mas[4][1], mas[6][1]) = \max(8, 10) = 10 — bu seqmentindəki maksimumdur.

sorğusunu nəzərdən keçirək. Seqmentin uzunluğu -dir, -dən böyük olmayan ən böyük ikinin qüvvəti -dür, ona görə k = 2. seqmenti və seqmentləri ilə örtülür, bunlar seqmentində üst-üstə düşür:

Cavab \max(mas[2][2], mas[3][2]) = \max(8, 8) = 8-dir. elementi hər iki seqmentə aiddir, lakin bu maksimumun dəyərinə təsir etmir.

Misaldakı bütün sorğular cədvəldə sadalanmışdır:

sorğu

seqmentlər

maksimumlar

cavab

,

,

,

,

,

,

,

,

və uzunluqlu seqmentlər üçün (, və sorğuları), hər iki örtücü seqment sorğu seqmentinin özü ilə üst-üstə düşür.

Alqoritmin tətbiqi — maksimumun indeksi

a_i (n \le 10^5) giriş ardıcıllığı massivində indeksindən başlayaraq saxlanılır. Bu variantda mas[i][j] xanası [i; i + 2^j - 1] seqmentindəki maksimum elementin indeksini saxlayır, maksimumun özü isə a[mas[i][j]]-ə bərabərdir. 2^{16} \le 10^5 < 2^{17} olduğundan, ikinci ölçü üçün LOGMAX = 17 kifayətdir.

#define MAX 100010
#define LOGMAX 17
int mas[MAX][LOGMAX];
int a[MAX];

Build_RMQ_Array funksiyası indekslərin sparse table-ını qurur.

void Build_RMQ_Array()
{

j = 0 üçün seqment bir elementdən ibarətdir, ona görə mas[i][0] = i.

  for (int i = 1; i <= n; i++) mas[i][0] = i;

j > 0 üçün, massivinin mas[i][j - 1] və mas[i + 2^{j-1}][j - 1] indeksli elementlərini — seqmentin iki yarısındakı maksimumları — müqayisə edin və böyüyünün indeksini saxlayın.

  for (int j = 1; (1 << j) <= n; j++)
    for (int i = 1; i + (1 << j) - 1 <= n; i++)
      if (a[mas[i][j - 1]] > a[mas[i + (1 << (j - 1))][j - 1]])
        mas[i][j] = mas[i][j - 1];
      else mas[i][j] = mas[i + (1 << (j - 1))][j - 1];
}

RangeMaxQuery funksiyası seqmentindəki maksimum elementin indeksini qaytarır.

int RangeMaxQuery(int i, int j)
{
  int temp, k = 0;

Əgər i > j olarsa, sərhədlər dəyişdirilir.

  if (i > j) temp = i, i = j, j = temp;

Sonra, dövrdə 2^k \le j - i + 1 şərtini ödəyən ən böyük -nı tapın.

  while ((1 << (k + 1)) <= j - i + 1) k++;

mas[i][k] və mas[j - 2^k + 1][k] indeksli elementləri müqayisə edin və böyüyünün indeksini qaytarın.

  return (a[mas[i][k]] >= a[mas[j - (1 << k) + 1][k]]) ?
         mas[i][k] : mas[j - (1 << k) + 1][k];
}

Proqramın əsas hissəsi. Massivin ölçüsü -i və elementlərini indeksindən başlayaraq oxuyun.

scanf("%d", &n);
for (i = 1; i <= n; i++)
  scanf("%d", &a[i]);

massivindən sparse table-ını qurun.

Build_RMQ_Array();

Sorğuların sayı -nu oxuyun.

scanf("%d", &q);

Hər sorğu üçün RangeMaxQuery funksiyası seqmentindəki maksimum elementin indeksini qaytarır, biz isə elementinin özünü çap edirik.

for (i = 0; i < q; i++)
{
  scanf("%d %d", &u, &v);
  int index = RangeMaxQuery(u, v);
  printf("%d\n", a[index]);
}

Alqoritmin tətbiqi — maksimum

a_i (n \le 10^5) giriş ardıcıllığı massivində indeksindən başlayaraq saxlanılır. Bu variantda mas[i][j] xanası [i; i + 2^j - 1] seqmentindəki maksimumun özünü saxlayır. 2^{16} \le 10^5 < 2^{17} olduğundan, ikinci ölçü üçün LOGMAX = 17 kifayətdir.

#define MAX 100010
#define LOGMAX 17
int mas[MAX][LOGMAX];
int a[MAX];

Build_RMQ_Array funksiyası massivindən sparse table-ı qurur:

mas[i][j] = \max(b[i], b[i + 1], \ldots, b[i + 2^j - 1])
void Build_RMQ_Array(int *b)
{

j = 0 üçün sparse table orijinal massivin elementlərini ehtiva edir: mas[i][0] = b[i].

  for (int i = 1; i <= n; i++) mas[i][0] = b[i];

j > 0 üçün mas[i][j] dəyəri seqmentin iki yarısındakı maksimumların böyüyünə bərabərdir.

  for (int j = 1; 1 << j <= n; j++)
    for (int i = 1; i + (1 << j) - 1 <= n; i++)
      mas[i][j] = max(mas[i][j - 1], mas[i + (1 << (j - 1))][j - 1]);
}

RangeMaxQuery funksiyası seqmentindəki maksimumu qaytarır.

int RangeMaxQuery(int i, int j)
{
  int temp, k = 0;

Əgər i > j olarsa, sərhədlər dəyişdirilir.

  if (i > j) temp = i, i = j, j = temp;

Dövrdə 2^k \le j - i + 1 şərtini ödəyən ən böyük -nı tapın. Bu üsulla -nın hesablanması loqarifm funksiyasını tələb etmir və üzən vergül xətalarından asılı deyil.

  while ((1 << (k + 1)) <= j - i + 1) k++;

Cavab [i; i + 2^k - 1] və [j - 2^k + 1; j] seqmentlərindəki maksimumların böyüyüdür.

  return max(mas[i][k],mas[j - (1<<k) + 1][k]);
}

Proqramın əsas hissəsi. Massivin ölçüsü -i və elementlərini indeksindən başlayaraq oxuyun.

scanf("%d",&n);
for(i = 1; i <= n; i++) scanf("%d",&a[i]);

massivindən sparse table-ını qurun.

Build_RMQ_Array(a);

Sorğuların sayı -nu oxuyun. Hər sorğu üçün seqmentindəki maksimumu çap edin.

scanf("%d",&q);
for(i = 0; i < q; i++)
{
  scanf("%d %d",&u,&v);
  printf("%d\n",RangeMaxQuery(u,v));
}