---
# System prepended metadata

title: TIN HỌC TRẺ 2024 - VÒNG KHU VỰC - BẢNG B

---

## Bài 1: Queens

### Hint:
- Dễ dàng nhận thấy, giới hạn của $n$ và $k$ là rất nhỏ, nên ta có thể làm theo tư tưởng đệ quy

### Subtask 1: $(k = 1)$
- Bây giờ chỉ cần đặt một con hậu, mà cần đặt sao cho tổng trọng số của các ô có quân hậu đặt là lớn nhất, suy ra ta tham lam, đặt con hậu ở vị trí có trọng số lớn nhất
```cpp=
int mx = INT_MIN;
for (int i = 1; i <= n; i++){
    for (int j = 1; j <= n; j++){
        mx = max(mx, a[i][j]);
    }
}
```

### Subtask 2: $(k \leq 3)$
- Nhận thấy số lượng ô tối đa của bảng là $8 \times 8 = 64$ mà $64 ^ 3 \approx 2 \times 10 ^ 5$
- Vì vậy, ta có thể thử mọi cách xếp các con hậu vào bảng
```cpp=
int mx = INT_MIN;
pair <int, int> vt[3];
void dequy(int x, int y, int tong, int sl){
    if (sl > 3) return ;
    if (x == n && y == n + 1){
        if (sl == 3){
            //Check xem các con hậu có trùng nhau không?
            //Các con hậu ở vị trí vt[0], vt[1], vt[2]
            //Nếu không trùng thì mx = max(mx, tong);
        }
        return ;
    }
    int newx = x, newy = y;
    if (newy == n + 1) newx++, newy = 1;
    dequy(newx, newy, tong, sl);
    vt[sl].fi = x, vt[sl].se = y;
    dequy(newx, newy, tong + a[x][y], sl + 1);
}
//Gọi hàm dequy(1, 1, 0, 0);
```

### Subtask 3: $(k \leq 8)$
- Thay vì sinh xong rồi kiểm tra, thì chúng ta có thể vừa sinh vừa kiểm tra
- Ta có đường chéo từ trái sang phải, từ trên xuống dưới, gọi tắt là $cheo1$ và đường chéo từ phải sang trái. từ trên xuống dưới, gọi tắt là $cheo2$. Bây giờ chỉ còn việc từ ô $x, y$, xem ô đó sẽ là loại đường chéo nào ở $cheo1$ và $cheo2$
- Về $cheo1$, lấy ví dụ các ô cùng đường $cheo1$ thì ta có $(1, 1), (2, 2), (3, 3)$ hoặc $(1, 2), (2, 3), (3, 4)$. Nhận thấy rằng từ ô $(x, y)$, ta có thể cộng cả hai $x, y$ với $1$ để tạo ra các ô có cùng đường $cheo1$. Có thể nối dài thêm các ô và ta có thể mã hoá các loại đường $cheo1$ là giá trị $y$ ở hàng thứ $1$, hay là mã hoá thành $y - (x - 1)$
![image](https://hackmd.io/_uploads/H14k1kRuA.png)
- Về $cheo2$, lấy ví dụ các ô cùng đường $cheo2$ thì ta có $(1, 3), (2, 2), (3, 1)$ hoặc $(2, 4), (3, 3), (4, 2)$. Nhận thấy rằng từ ô $(x, y)$, ta có thể đồng thời cộng $x$ và trừ $y$ với $1$ để tạo ra các ô có cùng đường $cheo2$. Có thể nối dài thêm các ô và ta có thể mã hoá các loại đường $cheo2$ là giá trị $y$ ở hàng thứ $1$, hay là mã hoá thành $y + (x - 1)$
![image](https://hackmd.io/_uploads/rkuhW1A_R.png)
- Vấn đề là các loại đường $cheo1$, có thể là số âm, nên ta có thể dùng $map$ hoặc cộng thêm một biến $bonus$
- Tạo mảng $hang[x]$, $cot[x]$, $cheo1[x]$, $cheo2[x]$ để kiểm tra xem hàng $x$, cột $x$, $cheo1$ và $cheo2$ có quân hậu nào không, nhưng ta có thể tối ưu bằng nhận xét rằng, mỗi con hậu sẽ ở những hàng riêng biệt, vì vậy nên ta không cần lưu mảng $hang[x]$
```cpp=
int bonus = 100, mx = INT_MIN;
bool cot[10], cheo1[1003], cheo2[1003];
int tinh1(int x, int y){
    return (y - (x - 1) + bonus);
}
int tinh2(int x, int y){
    return (y + (x - 1) + bonus);
}
void dequy(int i, int tong, int dem){
    //cout << i << " " << tong << " " << dem << "\n";
    if (i == n + 1){
        //cout << dem << "\n";
        if (dem == k) mx = max(mx, tong);
        return ;
    }
    dequy(i + 1, tong, dem);
    for (int j = 1; j <= n; j++){
        if (!cot[j] && !cheo1[tinh1(i, j)] && !cheo2[tinh2(i, j)]){
            cot[j] = true, cheo1[tinh1(i, j)] = true, cheo2[tinh2(i, j)] = true;
            dequy(i + 1, tong + a[i][j], dem + 1);
            cot[j] = false, cheo1[tinh1(i, j)] = false, cheo2[tinh2(i, j)] = false;
        }
    }
}
//dequy(1, 0, 0);
//Code của tknhatbm trong thi :)
```

## Bài 2: Akseq

### Hint:
- Dễ dàng nhận thấy nếu thoả mãn ở vị trí $i$ thì cũng sẽ thoả mãn ở vị trí $j$ nếu $j = i +  xn$ với $x$ là số thoả mãn $i + x \times n + d - 1 \leq n \times k$ hoặc từ vị trí $i$ nếu thoả mãn thì sẽ có $\lfloor (n \times k + 1 - d - i) \div x \rfloor + 1$ vị trí như vậy (tính cả $i$)

### Subtask 1: $(n * k \leq 3 * 10 ^ 3)$
- Ta có thể tạo mảng $A^k$ rồi sau đó duyệt từng đoạn có độ dài $d$, và kiểm tra điều kiện
```cpp=
// Tự tạo mảng A ^ k nhé :)
ll m = n * k, dem = 0;
for (int i = 1; i + d - 1 <= m; i++){
    ll tong = 0, mn = LLONG_MAX;
    for (int j = i; j <= i + d - 1; j++) tong += a[j], mn = min(mn, a[j]);
    if (2 * mn * d > tong) dem++;
}
```

### Subtask 2: $(n * k \leq 4 * 10 ^ 5)$
- Với tổng của dãy thì ta có thể dùng kĩ thuật **sliding window**, còn về min thì ta có thể xử lý bài toán con **Tìm Max/Min trong đoạn tịnh tiến**, dùng **Deque, Multiset, Segment Tree, Sparse Table, ...**
- https://wiki.vnoi.info/algo/data-structures/deque-min-max
```cpp=
ll m = n * k, dem = 0;
for (int i = 1; i + d - 1 <= m; i++){
    ll tong = S[i + d - 1] - S[i - 1], mn = get(i, i + d - 1);
    if (2 * mn * d > tong) dem++;
}
```

### Subtask 3: $(n * k \leq 5 * 10 ^ 7)$
- Giống **Subtask 2**, nhưng hãy lưu ý rằng có thể bị quá thời gian do dùng cấu trúc dữ liệu quá chậm như **Multiset**, khuyên nên dùng **Deque**
```cpp=
ll m = n * k, dem = 0;
for (int i = 1; i + d - 1 <= m; i++){
    ll tong = S[i + d - 1] - S[i - 1], mn = get(i, i + d - 1);
    if (2 * mn * d > tong) dem++;
}
```

### Subtask 4: $(n * k \leq 5 * 10 ^ {10})$
- Áp dụng gợi ý, ta chỉ cần duyệt các vị trí $i$ trong khoảng $1 \rightarrow n$, nếu thoả thì $dem$ tăng lên số vị trí như vậy
- Với mỗi đoạn từ $i \rightarrow i + d - 1$ thì ta xét hai trường hợp
    - Nếu $i + d - 1 \leq n$ thì ta xét như **Subtask 2, 3**
    - Nếu $i + d - 1 > n$ thì ta sẽ đặt $kt = i + d - 1$ và chia đoạn ra như sau
    ![image](https://hackmd.io/_uploads/SJu3E-C_0.png)
    - Nhận thấy giờ với mỗi đoạn, ta có thể tính dễ dàng
```cpp=
m = n * k;
for (int i = 1; i <= n; i++){
    int bd = i, kt = i + d - 1;
    if (kt > m) continue;
    if (kt <= n){
        ll vt = 2 * get(bd, kt) * d;
        ll vp = S[kt] - S[bd - 1];
        bool check = (vt > vp);
        dem += check * k;
        continue;
    }
    ll u = 1, v = kt % n, tong = 0, mn = LLONG_MAX;
    if (v == 0) v = n;
    tong += S[n] - S[bd - 1];
    tong += S[v] - S[u - 1];
    mn = min(get(bd, n), get(u, v));
    ll oL = n + 1, oR = kt - v;
    if (oL <= oR){
        ll sd = (oR - oL + 1) / n;
        tong += sd * S[n];
        mn = get(1, n);
    }
    ll vt = 2 * mn * d;
    ll vp = tong;
    bool check = (vt > vp);
    dem += check * ((m + 1 - d - i) / n + 1);
}
//Code của tknhatbm trong thi :)
```