Problem Müəllifi: | Andriy Kholod (Connect) |
Hazırlayan: | Andriy Kholod (Connect) |
Analiz: | Andriy Kholod (Connect) |
Fikir
İkinci mərhələdə yalnız alt massivləri onların cəminə sıxışdıra bilərik. Bu, son massiv [x,x,x]-nin həmişə ilkin massivdən üç boş olmayan ardıcıl hissəyə bərabər cəmlərlə bölünməsini ifadə edir.
Yəni, birinci mərhələdə bütün əvəzləmələrdən sonra massiv belə olmalıdır ki, 1 \le i < j < n indeksləri mövcud olsun və aşağıdakı şərt ödənilsin:
\sum_{k=1}^{i} a_k = \sum_{k=i+1}^{j} a_k = \sum_{k=j+1}^{n} a_k
Beləliklə, problem əslində: massivin üç boş olmayan alt massivə bərabər cəmlərlə bölünməsi üçün minimum sayda elementi dəyişməkdir.
Niyə cavab 2-dən böyük deyil
Həmişə iki elementdən çoxunu dəyişə bilməzsiniz.
Həqiqətən, üç boş olmayan hissəyə istənilən bölməni götürün, məsələn: { {a_1}, {a_2}, {a_3,\dots,a_n} } Sonra birinci hissədə bir elementi və ikinci hissədə bir elementi dəyişə bilərsiniz ki, bütün üç hissənin cəmləri bərabər olsun.
Buna görə də, cavab həmişə aşağıdakilərdən biridir: [ 0,\ 1,\ 2 ]
Beləliklə, yoxlamağı öyrənmək kifayətdir:
cavabın olub-olmadığını;
cavabın olub-olmadığını;
əks halda, cavabın olduğunu.
0 dəyişiklik halı
Tutaq ki, S = \sum_{i=1}^{n} a_i
Heç bir elementi dəyişməsək, onda massiv artıq üç bərabər cəmlə bölünə bilməlidir. Beləliklə, aşağıdakı şərt ödənilməlidir: S \equiv 0 \pmod 3 və hər hissənin cəmi x = S/3
Bu, prefiks cəmləri istifadə edərək çox sadə bir şəkildə yoxlanıla bilər:
cəminə sahib bir prefiks axtarın;
sonra daha sağda cəminə sahib bir prefiks axtarın.
Hər ikisi mövcud olduqda və üçüncü hissə boş olmadıqda, cavab -dır.
Bir dəyişiklik kifayət edəndə
Tutaq ki, tam olaraq bir elementi dəyişmək istəyirik. Ondan sonra massiv üç bərabər cəmlə bölünməlidir.
Əsas müşahidə: yalnız bir element dəyişdirilirsə, o, dəqiq üç hissədən birində yerləşir. Beləliklə, digər iki hissənin artıq bərabər cəmləri olmalıdır. Üçüncü hissəni bu dəyərə bir dəyişikliklə "çəkə" bilərik.
Buna görə də, iki hissənin cəmlərinin artıq bərabər olduğu üç boş olmayan hissəyə bölmənin olub olmadığını yoxlamalıyıq.
İkinci kəsik mövqesindən sonra olsun, burada 2 \le j < n Sonra üç hissə: { {1..i},\ {i+1..j},\ {j+1..n} } bəziləri üçün i < j.
İki hissənin bərabərliyi üçün üç halı nəzərdən keçirək.
1) Birinci və ikinci hissələr bərabərdir
Onda: \text{pref}[i] = \text{pref}[j] - \text{pref}[i] yəni, 2 \cdot \text{pref}[i] = \text{pref}[j] və ya \text{pref}[i] = \frac{\text{pref}[j]}{2}
Beləliklə, sabit üçün, prefiksin daha əvvəl görüldüyünü və -nin cüt olduğunu bilmək kifayətdir.
2) İkinci və üçüncü hissələr bərabərdir
Onda: \text{pref}[j] - \text{pref}[i] = S - \text{pref}[j] Bundan: \text{pref}[i] = 2 \cdot \text{pref}[j] - S
Beləliklə, belə bir prefiksin daha əvvəl olub olmadığını yoxlamalıyıq.
3) Birinci və üçüncü hissələr bərabərdir
Onda: \text{pref}[i] = S - \text{pref}[j]
Yəni, belə bir prefiksin daha əvvəl olub olmadığını yoxlamaq da kifayətdir.
Niyə bu yoxlamalar kifayətdir
Hər üçün, i < j olan bütün mümkün -ləri artıq görüldüyü prefiks cəmləri vasitəsilə nəzərdən keçiririk.
seen dəsti bütün dəyərləri ehtiva edir:
\text{pref}[1], \text{pref}[2], \dots, \text{pref}[j-1]
Beləliklə, yuxarıdakı üç şərtdən hər biri -də sualı cavablayır:
birinci və ikinci hissələrin bərabər olduğu bir -nin olub olmadığını;
ikinci və üçüncü hissələrin bərabər olduğu bir -nin olub olmadığını;
birinci və üçüncü hissələrin bərabər olduğu bir -nin olub olmadığını.
Əgər bu şərtlərdən ən az biri ödənilirsə, onda iki hissənin artıq bərabər cəmləri var və üçüncüsü bir əvəzləmə ilə düzəldilə bilər.
Beləliklə, cavab -dir.
Niyə iterasiya dəqiq j = 2 \dots n-1 üçün gedir
Biz üç hissənin də boş olmalarını istəyirik:
birinci hissə ən azı bir elementi ehtiva etməlidir \Rightarrow i \ge 1;
ikinci hissə boş olmamalıdır \Rightarrow i < j;
üçüncü hissə boş olmamalıdır \Rightarrow j < n.
Buna görə də, xarici döngü
2 \le j < n
üzərində gedir və yoxlamadan əvvəl \text{pref}[j-1] seenə əlavə olunur ki, indeksinin mütləq -dən kiçik olduğunu təmin etsin.
Son Alqoritm
Hər test halı üçün:
Prefiks cəmlərini hesablayın.
Dəyişiklik etmədən üç bərabər cəmi dərhal əldə etməyin mümkün olub olmadığını yoxlayın. Əgər belədirsə, cavab -dır.
Əks halda, ikinci sərhəd -ni iterasiya edin və prefiks dəsti istifadə edərək bir dəyişiklik üçün üç şərti yoxlayın. Əgər ən az biri ödənilirsə, cavab -dir.
Əks halda, cavab -dir.
Düzgünlük
Artıq sübut etmişik:
əgər dəyişiklik etmədən üç bərabər cəmlə bölmə varsa, kod -ı çıxaracaq;
əgər bu mümkün deyilsə, amma iki hissənin artıq bərabər cəmləri olan bir bölmə varsa, kod bunu tapacaq və -i çıxaracaq;
əgər nə , nə də uyğun deyilsə, onda həmişə dəyişikliklə cavabı əldə etmək mümkündür.
Beləliklə, alqoritm həmişə minimum dəyişiklik sayını tapır.
Asimptotiklər
Hər test halı üçün:
prefiks cəmlərini hesablamaq — ;
halını yoxlamaq — ;
unordered_set istifadə edərək halını yoxlamaq — ortalama .
Beləliklə, ümumi mürəkkəblik: orta hesabla, və bütün testlər üzrə: O\left(\sum n\right)
Yaddaş:
Həll Kodu
#include <bits/stdc++.h>
#include <random>
// fast_pragmas
//#pragma GCC optimize("O3")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize ("unroll-loops")
//#pragma GCC target("avx,avx2,tune=native")
// fast_pragmas ended
//#pragma comment(linker, "/stack:200000000")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("O3,unroll-loops")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4")
//#pragma GCC target("avx2,bmi,bmi2,popcnt,lzcnt")
using namespace std;
#define ull unsigned long long
#define ll long long
#define ld long double
#define speed ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define endl '\n'
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define pb push_back
#define ln log
#define lg log10
#define log log2
#define here cout<<"HERE"<<endl
const ll clasik = 5e5 + 7;
const ll mln = 1e6 + 7;
const ll mod = 1e9 + 7;
//const ll mod = 998244353;
const ll block_size = 450;
const ll INF = 1e17;
ll df[clasik];
void solve() {
ll n;
cin >> n;
vector<ll> a(n + 1), pref(n + 1, 0);
for (int i = 1; i <= n; i++) {
cin >> a[i];
pref[i] = pref[i - 1] + a[i];
}
ll S = pref[n];
bool zero = false;
if (S % 3 == 0) {
ll x = S / 3;
bool first_found = false;
for (int i = 1; i < n; i++) {
if (pref[i] == x) first_found = true;
if (pref[i] == 2 * x && first_found) {
zero = true;
break;
}
}
}
if (zero) {
cout << 0 << endl;
return;
}
bool one = false;
unordered_set<ll> seen;
seen.reserve(n * 2);
for (int j = 2; j < n; j++) {
seen.insert(pref[j - 1]);
if (pref[j] % 2 == 0 && seen.count(pref[j] / 2)) {
one = true;
break;
}
if (seen.count(2 * pref[j] - S)) {
one = true;
break;
}
if (seen.count(S - pref[j])) {
one = true;
break;
}
}
if (one) cout << 1 << endl;
else cout << 2 << endl;
}
signed main() {
//freopen("deciding_points_input.txt" , "r" , stdin);
//freopen("outputA.txt" , "w" , stdout);
speed;
int test;
test = 1;
//precalc();
cin >> test;
for (int tt = 1; tt <= test; tt++) {
//cout << "Case #" << tt << ": ";
solve();
}
return 0;
}