Bedao Mini Contest 06 - YUGIOH
===
[https://oj.vnoi.info/problem/bedao_m06_yugioh](https://oj.vnoi.info/problem/bedao_m06_yugioh)
-----
### Hướng dẫn
Để kiểm tra một số bất kì có phải là số nguyên tố không quá $X$ hay không, ta chỉ cần sàng các số nguyên tố trong đoạn $[1, X]$ và có thể kiểm tra trong $O(1)$
Yêu cầu đề bài là chuyển các số $a_i$ nguyên tố $\leq X$ lại gần nhau
$\Rightarrow$ Có thể coi mảng $a[]$ như $n$ số nhị phân, với giá trị $1$ khi $a_i$ nguyên tố $\leq X$ và giá trị $0$ với các số còn lại.
$\Rightarrow$ Gọi $k$ là số số $1$ trong dãy. Lúc này ta chỉ cần đưa $k$ số $1$ kề nhau với ít lần swap nhất
Vì các số $1$ kề nhau nên ta xét các đoạn $[l, r]$ với $(1 \leq l \leq r \leq n)$ và ($r - l + 1 = k)$, ta thử đưa hết các số $1$ vào trong đoạn $[l, r]$
Vì ta cần số lần swap tối thiểu nên ta chỉ swap các số $1$ ở ngoài đoạn với các số $0$ ở trong đoạn.
Nhận thấy rằng số lần swap đó cũng chính bằng số số $0$ ở trong đoạn. Nên ta có thể dựng mảng cộng dồn để đếm số số $0$ với mỗi đoạn $[l, r]$ bất kì trong $O(1)$
-----
### Độ phức tạp
Vậy độ phức tạp bài này là $O(n + X)$, trong đó:
- Mất $O(X)$ sàng nguyên tố $X$ số đầu
- Mất $O(n)$ để nhận dữ liệu và chuyển về dạng nhị phân
- Mất $O(n)$ để dựng lên mảng tiền tố
- Mất $O(n)$ để duyệt qua các đoạn $[l, r]$ và cập nhật kết quả số lần swap tối thiểu
-----
### Code
```cpp=
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
/// Linear SPyofgame Sieve
vector<int> prime; /// prime list
vector<int> lpf; /// lowest prime factor
void sieve(int n)
{
if (n <= 1) return ;
prime.assign(1, 2);
lpf.assign(n + 1, 2);
for (int x = 3; x <= n; x += 2)
{
if (lpf[x] == 2) prime.push_back(lpf[x] = x);
for (int i = 0; i < prime.size() && prime[i] <= lpf[x] && prime[i] * x <= n; ++i)
lpf[prime[i] * x] = prime[i];
}
}
/// O(1) check - require sieving
bool isPrime(int x)
{
if (x < 2 || x >= lpf.size()) return false;
return lpf[x] == x;
}
const int LIM = 1e6 + 16;
int n, k;
int a[LIM];
int f[LIM];
int main()
{
/// Nhap nhanh
ios::sync_with_stdio(NULL);
cin.tie(NULL);
/// Input
cin >> n >> k;
for (int i = 1; i <= n; ++i)
cin >> a[i];
/// Sang nguyen to
sieve(k);
/// Tien xu li
f[0] = 0;
for (int i = 1; i <= n; ++i)
{
a[i] = (a[i] <= k && isPrime(a[i])); // Bien mang a[] thanh day nhi phan
f[i] = f[i - 1] + a[i]; // Dung mang cong don
}
int cnt_ones = f[n]; // So gia tri (a[i] = 1)
/// Tinh ket qua
int best = cnt_ones; // thuc te chi can d - 1
for (int l = 1, r = cnt_ones; r <= n; ++l, ++r) // duyet qua cac doan [l, r] do dai (d)
{
int cnt_zeros = (f[r] - f[l - 1]); // so so 0 tren doan
best = min(best, cnt_ones - cnt_zeros); // cap nhat ket qua
}
/// Output
cout << best;
return 0;
}
```