Məsələdə generasiyadan sonra dəyişməyən massivin bir seqmentində minimumun təkrar-təkrar tapılması tələb olunur. Sorğuların sayı 10^7-ə çatır, ona görə hər bir sorğu vaxtında emal olunmalıdır. Bunun üçün biz sparse table — RMQ (Range Minimum Query) məsələsi üçün statik məlumat strukturundan istifadə edirik.
Cədvəlin qurulması. Hər bir mövqeyi və i + 2^j - 1 \le n şərtini ödəyən hər bir üçün mas[i][j] xanasında [i; i + 2^j - 1] seqmentinin (uzunluğu 2^j) minimum elementinin indeksi saxlanılır. j = 0 üçün seqment bir elementdən ibarətdir, ona görə mas[i][0] = i.
j > 0 üçün uzunluğu 2^j olan seqmenti uzunluğu 2^{j-1} olan iki yarıya bölürük: [i; i + 2^{j-1} - 1] və [i + 2^{j-1}; i + 2^j - 1]. Yarılardakı minimumların indeksləri artıq hesablanıb — bunlar mas[i][j - 1] və mas[i + 2^{j-1}][j - 1]-dir:
Bütün seqmentin minimumu yarıların iki minimumundan kiçik olanına bərabərdir. Buna görə mas[i][j] bu iki indeksdən – mas[i][j - 1] və mas[i + 2^{j-1}][j - 1] – hansı daha kiçik massiv elementini göstərirsə ona bərabər olur. Cədvəl -nin artma ardıcıllığı ilə doldurulur və O(n \log n) xana saxlayır.
Sorğuya cavab vermək. Tutaq ki, seqmentində minimumu tapmaq lazımdır. k = \lfloor \log_2(v - u + 1) \rfloor olsun – seqmentin uzunluğunu aşmayan ən böyük ikinin qüvvətinin göstəricisi. -dan başlayan uzunluğu 2^k olan seqment və -də bitən uzunluğu 2^k olan seqment birlikdə -ni tamamilə örtür, çünki 2 \cdot 2^k > v - u + 1:
Bu seqmentlər üst-üstə düşə bilər, lakin bu, minimum üçün heç bir əhəmiyyət daşımır: iki dəfə hesablanan element nəticəni dəyişmir (məsələn, cəmdən fərqli olaraq). Deməli, -dəki minimum a[mas[u][k]] və a[mas[v - 2^k + 1][k]] elementlərindən kiçik olanına bərabərdir, və sorğu vaxtında emal olunur.
Generasiya olunan sorğuların və sərhədləri istənilən ardıcıllıqla gələ bilər, ona görə sorğunu yerinə yetirməzdən əvvəl onları zərurət olduqda yer dəyişdiririk.
Mürəkkəblik. Cədvəlin qurulması O(n \log n) vaxt aparır, hər bir sorğu isə vaxtında işlənir. Ümumi mürəkkəblik O(n \log n + m)-dir. m \le 10^7 olduğundan, məhz sorğu vaxtı zaman limitinə sığmağa imkan verir.
Misal
Misala baxaq: n = 10, m = 8, a_1 = 12345. Generasiya olunmuş massiv məsələnin şərtindəki massivlə üst-üstə düşür. Bunun üçün cədvəli aşağıda göstərilmişdir. Xanalarda minimum elementlərin indeksləri yazılmışdır; tire işarəsi seqmentin massivin hüdudlarından kənara çıxdığını bildirir.
\boldsymbol{j \setminus i} | ||||||||||
— | ||||||||||
— | — | — | ||||||||
— | — | — | — | — | — | — |
Məsələn, mas[3][2] = 5: seqmentində minimum element a_5 = 1565158-dir. mas[2][3] = 2 dəyəri mas[2][2] = 2 və mas[6][2] = 8-dən alınır, çünki a_2 = 305498 < a_8 = 570265.
Birinci sorğu: u_1 = 3, v_1 = 9. Seqmentin uzunluğu -dir, ona görə k = 2. seqmenti və seqmentləri ilə örtülür, onlar üçün mas[3][2] = 5 və mas[6][2] = 8-dir. a_8 = 570265 < a_5 = 1565158 olduğundan, cavab ans_1 = 570265-dir.
İkinci sorğunun parametrləri:
u_2 > v_2 olduğundan, sərhədlər yer dəyişdirilir, və seqmentindəki minimum hesablanır.
Bütün sorğular cədvəldə göstərilmişdir:
\boldsymbol{u_i} | \boldsymbol{v_i} | seqment | \boldsymbol{ans_i} | |
Son sorğunun parametrləri u_8 = 5, v_8 = 3-dür, və onun cavabı ans_8 = 1565158-dir. Məhz bu qiymətlər çap olunur. Qeyd edək ki, seqmentin sıralanmış sərhədləri deyil, u_8 və v_8-in ilkin qiymətləri çap olunur.
Alqoritmin reallaşdırılması
massivinin elementləri (n \le 10^5) -ci indeksdən başlayaraq saxlanılır. massivi ədədinin əsası üzrə loqarifminin tam hissəsini saxlayır.
#define MAX 100001
#define LOGMAX 17
int a[MAX], lg2[MAX];Sparse table massivində saxlanılır: mas[i][j] – [i; i + 2^j - 1] seqmentinin minimum elementinin indeksidir. 2^{16} \le 10^5 < 2^{17} olduğundan, -nin qiymətləri -dan artıq deyil, ona görə ikinci ölçü üçün LOGMAX = 17 kifayətdir.
int mas[MAX][LOGMAX];check funksiyası u > v olduqda və -nin qiymətlərini yerini dəyişir ki, u \le v olsun. Parametrlər istinad ilə ötürülür.
void check(int &u, int &v)
{
int temp;
if (u > v) temp = u, u = v, v = temp;
}RMQ_nlogn funksiyası sparse table-ı qurur.
void RMQ_nlogn(void)
{-dən -ə qədər bütün ədədlər üçün \lfloor log_2 i \rfloor tam loqarifm qiymətlərini hesablayırıq.
for (int i = 2; i <= n; i++) lg2[i] = lg2[i / 2] + 1;Əvvəlcə bütün üçün mas[i][0] = i.
for (int i = 1; i <= n; i++) mas[i][0] = i;Sonra, 2^j \le n şərti ödəndikcə hər bir üçün və [i; i + 2^j - 1] seqmenti massivin hüdudlarından kənara çıxmadıqca hər bir üçün, mas[i][j - 1] və mas[i + 2^{j-1}][j - 1]-dən kiçik elementin indeksini seçirik.
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];
}q funksiyası seqmentindəki minimum elementin indeksini qaytarır. k = \lfloor \log_2(j - i + 1) \rfloor hesablanır və uzunluğu 2^k olan iki seqmentin minimumları müqayisə edilir: [i; i + 2^k - 1] və [j - 2^k + 1; j].
int q(int i, int j)
{
int k = lg2[j - i + 1];
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 ilk elementi a_1 və birinci sorğunun və parametrlərini oxuyuruq.
scanf("%d %d",&n,&m);
scanf("%d %d %d",&a[1],&u,&v);Məsələnin şərtindəki düsturdan istifadə edərək a_2, a_3, \ldots, a_n elementlərini generasiya edirik. 23 \cdot a_i hasili 23 \cdot 16714589 \approx 3.8 \cdot 10^8-dən aşmır və tipinə sığır.
for(i = 2; i <= n; i++)
a[i] = (23 * a[i-1] + 21563) % 16714589;sparse table-ını qururuq.
RMQ_nlogn();sorğunu bir-bir emal edirik.
for (i = 1; i <= m; i++)
{Cari sorğunun sərhədlərini və -ə köçürürük və zərurət olduqda check funksiyası ilə onları sıralayırıq. və -nin özləri dəyişdirilmir: növbəti sorğunun parametrləri onlardan hesablanır, və məhz onlar cavabda çap olunur.
u1 = u; v1 = v; check(u1,v1);[u1; v1] seqmentindəki minimum elementi tapırıq.
ans = a[q(u1,v1)];Əgər sorğu sonuncu deyilsə, məsələnin şərtindəki düsturlardan istifadə edərək növbəti sorğunun parametrlərini hesablayırıq.
if (i < m)
{
u = ((17*u + 751 + ans + 2*i) % n) + 1;
v = ((13*v + 593 + ans + 5*i) % n) + 1;
}
}Son sorğunun parametrlərini və onun cavabını çap edirik.
printf("%d %d %d\n",u,v,ans);