çiftlerinin sayısı n \cdot (n + 1) / 2'ye eşittir ve n = 2 \cdot 10^5 için yaklaşık 2 \cdot 10^{10}'dur, bu nedenle tüm aralıkları tek tek denemek imkânsızdır. Toplamı, diziyi en büyük elemanın konumundan bölerek "böl ve yönet" yöntemiyle hesaplayacağız.
En büyük elemana göre bölme. aralığının içinde kalan tüm aralıkları üzerinden P(i, j) toplamını bulmamız gerektiğini varsayalım. üzerindeki en büyük elemanın konumunu bulalım. Her [i; j] \subseteq [b; e] aralığı aşağıdaki üç türden tam olarak birine aittir:
konumunu içerir, yani b \le i \le m \le j \le e;
tamamen [b; m - 1] içinde yer alır;
tamamen [m + 1; e] içinde yer alır.
İkinci ve üçüncü türden aralıklar özyinelemeli olarak işlenir. Birinci türden aralıklar için en büyük değer a_m'ye eşittir, dolayısıyla M(i, j) = a_m olur ve bunların toplam katkısı
Geriye konumunu içeren tüm aralıklar üzerinden EBOB'ların toplamını hızlıca hesaplamak kalıyor.
EBOB'un ayrıştırılması. 'yi içeren bir aralığı, sol kısım ve sağ kısım 'den oluşur. Bu nedenle
Böylece bir aralığın EBOB'u bir değer çifti ile belirlenir: sol kısmın EBOB'u ve sağ kısmın EBOB'u.
Uç grupları. arttıkça G(m, j) değeri artmaz. Üstelik her değiştiğinde yeni değer öncekinin öz bölenidir ve bu nedenle ondan en az iki kat küçüktür. Dolayısıyla G(m, j) en fazla \lfloor \log_2 C \rfloor + 1 farklı değer alır; burada — bir elemanın alabileceği en büyük değerdir. C \le 10^9 için bu, en fazla \lfloor \log_2 C \rfloor + 1 = 30 değerdir.
G(m, j) değeri aynı olan konumları ardışık bir blok oluşturur. Böyle bir konum bloğuna grup diyelim. Her grup bir (g, \mathit{cnt}) çifti ile tanımlanır; burada — EBOB değeri, ise gruptaki konumların sayısıdır. Benzer şekilde sol uçlar da değerine göre gruplara ayrılır.
Bir grubun sonu ikili arama ile bulunur. Bir grup konumunda başlıyorsa ve g = G(m, j) ise, G(m, t) = g eşitliği 'den grubun sonuna kadar tüm 'ler için sağlanır ve sonrasındaki tüm 'ler için sağlanmaz.
Grup çiftleri üzerinden toplam. Sol grup (g_L, \mathit{cnt}_L), sağ grup ise (g_R, \mathit{cnt}_R) olsun. O zaman sol ucu sol grupta, sağ ucu sağ grupta bulunan tüm \mathit{cnt}_L \cdot \mathit{cnt}_R aralığın EBOB'u aynıdır ve \gcd(g_L, g_R)'ye eşittir. Bu nedenle
burada ikinci toplam bir sol ve bir sağ gruptan oluşan tüm çiftler üzerinden alınır. Bu tür çiftlerin sayısı 30 \cdot 30'u geçmez.
Aralık sorguları. En büyük elemanın konumunu bulmak ve bir aralıktaki EBOB'u hesaplamak için iki seyrek tablo (Sparse Table) kullanırız. Her iki işlem de idempotenttir: \max(x, x) = x ve \gcd(x, x) = x. Bu nedenle bir aralık sorgusu, 2^k uzunluğundaki iki örtüşen blok kullanılarak yanıtlanır.
'nın değeri çok büyüktür, bu yüzden tüm hesaplamalar 10^9 + 7 modülüne göre yapılır.
Karmaşıklık. Her özyinelemeli çağrı bir konumunu değerlendirmeden çıkarır, bu nedenle en fazla çağrı yapılır. Her çağrıda her iki tarafta en fazla O(\log C) grup bulunur. Bir grubun sonu O(\log n) EBOB sorgusu ile ikili aramayla bulunur ve her sorgu O(\log C) sürer. Grup çiftleri üzerinde dolaşmak O(\log^2 C) EBOB hesaplaması gerektirir. Seyrek tabloların oluşturulması O(n \log n \cdot \log C) sürer. Toplam karmaşıklık O(n \log^2 C \cdot (\log n + \log C))'dir. Pratikte grup sayısı 'dan çok daha azdır ve algoritma hızlı çalışır.
Örnek
İkinci örneği ele alalım: A = (2, 4, 6, 12, 3). Konumlar birden başlayarak numaralandırılır.
İlk çağrı tüm dizisini işler. En büyük eleman a_4 = 12'dir, dolayısıyla m = 4. Şekilde sol ve sağ uçların grupları gösterilmiştir.
Sol uçlar: G(4, 4) = 12, G(3, 4) = \gcd(6, 12) = 6, G(2, 4) = G(1, 4) = 2. , ve gruplarını elde ederiz.
Sağ uçlar: G(4, 4) = 12, G(4, 5) = \gcd(12, 3) = 3. ve gruplarını elde ederiz.
Tüm grup çiftlerini inceleyelim:
sol grup | sağ grup | EBOB | aralık sayısı | katkı |
(12, 1) | (12, 1) | 12 | 1 | 12 |
(12, 1) | (3, 1) | 3 | 1 | 3 |
(6, 1) | (12, 1) | 6 | 1 | 6 |
(6, 1) | (3, 1) | 3 | 1 | 3 |
(2, 2) | (12, 1) | 2 | 2 | 4 |
(2, 2) | (3, 1) | 1 | 2 | 2 |
konumunu içeren aralıklar üzerinden EBOB'ların toplamı 12 + 3 + 6 + 3 + 4 + 2 = 30'dur ve bu aralıkların katkısı 12 \cdot 30 = 360'tır. Örneğin, tablonun son satırı ve aralıklarına karşılık gelir: her biri için EBOB 1, en büyük değer ise 12'dir.
Ardından ve aralıkları özyinelemeli olarak işlenir. Tüm çağrılar tabloda verilmiştir:
aralık | \boldsymbol{a_m} | sol gruplar | sağ gruplar | EBOB toplamı | katkı | |
\boldsymbol{[1; 5]} | 4 | 12 | (12, 1), (6, 1), (2, 2) | (12, 1), (3, 1) | 30 | 360 |
\boldsymbol{[1; 3]} | 3 | 6 | (6, 1), (2, 2) | (6, 1) | 10 | 60 |
\boldsymbol{[1; 2]} | 2 | 4 | (4, 1), (2, 1) | (4, 1) | 6 | 24 |
\boldsymbol{[1; 1]} | 1 | 2 | (2, 1) | (2, 1) | 2 | 4 |
\boldsymbol{[5; 5]} | 5 | 3 | (3, 1) | (3, 1) | 3 | 9 |
Cevap 360 + 60 + 24 + 4 + 9 = 457'dir.
Algoritmanın Gerçekleştirilmesi
Sabitleri tanımlayalım:
\mathit{MOD} = 10^9 + 7 — modül,
\mathit{LOG} = 18 — seyrek tablolardaki en büyük üs (2^{18} > 2 \cdot 10^5).
ll tipini tanımlayalım.
#define MOD 1000000007LL
#define LOG 18 // 2^18 = 262144 > 2*10^5
typedef long long ll;Dizi içinde saklanır; elemanlar sıfırdan başlayarak indekslenir. değeri \lfloor \log_2 \mathit{len} \rfloor'e eşittir. \mathit{spMax}[i][k] seyrek tablosu en büyük elemanın indeksini, \mathit{spGcd}[i][k] ise [i; i + 2^k - 1] aralığındaki elemanların EBOB'unu saklar.
vector<int> a;
vector<int> lg2; // lg2[len] = floor(log2(len))
// spMax[i][k] — [i, i + 2^k) aralığındaki en büyük elemanın indeksi
vector<vector<int>> spMax;
// spGcd[i][k] — [i, i + 2^k) aralığındaki EBOB
vector<vector<int>> spGcd;gcd fonksiyonu iki sayının EBOB'unu Öklid algoritmasıyla hesaplar.
int gcd(int x, int y)
{
while (y) { int t = x % y; x = y; y = t; }
return x;
}better fonksiyonu, verilen iki ve indeksinden dizi elemanı daha büyük olanı döndürür. Elemanlar eşitse 'yi döndürür.
int better(int i, int j)
{
return a[i] >= a[j] ? i : j;
}build fonksiyonu dizisini doldurur ve her iki seyrek tabloyu O(n \log n \cdot \log C), C \le 10^9 sürede oluşturur.
void build()
{
lg2.assign(n + 1, 0);
for (int len = 2; len <= n; len++) lg2[len] = lg2[len / 2] + 1;
spMax.assign(n, vector<int>(LOG + 1));
spGcd.assign(n, vector<int>(LOG + 1));
for (int i = 0; i < n; i++)
{
spMax[i][0] = i;
spGcd[i][0] = a[i];
}
for (int k = 1; (1 << k) <= n; k++)
for (int i = 0; i + (1 << k) <= n; i++)
{
int h = 1 << (k - 1); // blok uzunluğunun yarısı
spMax[i][k] = better(spMax[i][k - 1], spMax[i + h][k - 1]);
spGcd[i][k] = gcd(spGcd[i][k - 1], spGcd[i + h][k - 1]);
}
}qMax fonksiyonu [l; r] aralığındaki en büyük elemanın indeksini döndürür.
int qMax(int l, int r)
{
int k = lg2[r - l + 1];
return better(spMax[l][k], spMax[r - (1 << k) + 1][k]);
}qGcd fonksiyonu [l; r] aralığındaki elemanların EBOB'unu döndürür. 2^k uzunluğundaki bloklar örtüşebilir, ancak \gcd(x, x) = x olduğundan bu sonucu etkilemez.
int qGcd(int l, int r)
{
int k = lg2[r - l + 1];
return gcd(spGcd[l][k], spGcd[r - (1 << k) + 1][k]);
}lastRight fonksiyonu G(m, t) = g olan en sağdaki t \in [\mathit{from}; e] konumunu bulur. Burada g = G(m, \mathit{from}). arttıkça artmadığından eşitlik aralığının bir başlangıç kısmında sağlanır ve ikili arama bu kısmın son konumunu bulur. Orta nokta (\mathit{lo} + \mathit{hi} + 1) / 2 olarak hesaplanır, böylece \mathit{lo} = \mathit{mid} atamasında arama takılıp kalmaz.
int lastRight(int m, int from, int e, int g)
{
int lo = from, hi = e;
while (lo < hi)
{
int mid = (lo + hi + 1) / 2;
if (qGcd(m, mid) == g) lo = mid; else hi = mid - 1;
}
return lo;
}firstLeft fonksiyonu simetrik olarak G(t, m) = g olan en soldaki t \in [b; \mathit{from}] konumunu bulur.
int firstLeft(int m, int from, int b, int g)
{
int lo = b, hi = from;
while (lo < hi)
{
int mid = (lo + hi) / 2;
if (qGcd(mid, m) == g) hi = mid; else lo = mid + 1;
}
return lo;
}Group yapısı aralık uçlarından oluşan bir grubu tanımlar:
— EBOB değeri,
— gruptaki konumların sayısı.
struct Group { int g; ll cnt; };solve fonksiyonu, içinde kalan tüm aralıkları üzerinden P(i, j) toplamını modülüne göre döndürür. Boş bir aralık için toplam 'dır.
ll solve(int b, int e)
{
if (b > e) return 0;üzerindeki en büyük elemanın konumunu buluruz.
int m = qMax(b, e);Sağ uçları j = m, m + 1, \ldots, e gruplara ayırırız. Mevcut için g = G(m, j) değerini hesaplar ve lastRight fonksiyonuyla grubun son konumunu buluruz. Grup t - j + 1 konum içerir; sonraki grup t + 1 konumundan başlar.
vector<Group> R;
for (int j = m; j <= e; )
{
int g = qGcd(m, j);
int t = lastRight(m, j, e, g);
R.push_back({ g, t - j + 1 });
j = t + 1;
}Benzer şekilde sol uçları i = m, m - 1, \ldots, b gruplara ayırırız. Grup 'den 'ye kadar olan konumları kaplar ve i - t + 1 konum içerir; sonraki grup t - 1 konumunda biter.
vector<Group> L;
for (int i = m; i >= b; )
{
int g = qGcd(i, m);
int t = firstLeft(m, i, b, g);
L.push_back({ g, i - t + 1 });
i = t - 1;
}konumunu içeren tüm aralıklar üzerinden EBOB değerlerinin toplamını hesaplarız. Her grup çifti için \gcd(l.g, r.g) \cdot l.\mathit{cnt} \cdot r.\mathit{cnt} ekleriz. l.\mathit{cnt} \cdot r.\mathit{cnt} çarpımı 4 \cdot 10^{10}'u geçmez ve long long tipine sığar. Bu çarpımı modülüne göre indirgeriz; bundan sonra EBOB'un (2^{30}'dan küçük) 'dan küçük bir sayıyla çarpımı da long long tipine sığar.
ll sumG = 0;
for (Group &l : L)
for (Group &r : R)
{
ll cnt = (l.cnt * r.cnt) % MOD;
sumG = (sumG + gcd(l.g, r.g) % MOD * cnt) % MOD;
}'yi içeren tüm aralıklarda en büyük değer a_m'dir, dolayısıyla bunların toplam katkısı a_m \cdot \mathit{sumG}'dir.
ll res = (ll)a[m] % MOD * sumG % MOD;konumunun tamamen solunda ve sağında kalan aralıklar için özyinelemeli olarak hesaplanan toplamları ekleriz.
return (res + solve(b, m - 1) + solve(m + 1, e)) % MOD;
}Programın ana kısmı. Giriş verilerini okuruz.
scanf("%d", &n);
a.resize(n);
for (int &x : a) scanf("%d", &x);Seyrek tabloları oluştururuz.
build();Tüm dizi — [0; n - 1] aralığı için cevabı yazdırırız.
printf("%lld\n", solve(0, n - 1));