---
# System prepended metadata

title: LQDOJ CUP 2022 - Round 6 - SOLDIER - Editorial

---

# LQDOJ CUP 2022 - Round 6 - SOLDIER - Editorial

**Author:** Nguyen Huu Nhat Quang - Le Quy Don High School for Gifted Students - Da Nang

**Tóm tắt đề:** Cho một dãy các phần tử $a_1, a_2, ..., a_n$ và một số nguyên dương $k$ ($n, k \leq 5000$). Một phần tử $a_i$ được xem là quan trọng khi và chỉ khi tồn tại một tập các phần tử thuộc dãy $a$ có chứa $a_i$, tạm gọi các phần tử này là $b_1, b_2, ..., b_j$, sao cho $b_1+b_2+...+b_j \ge k$, và $b_1+b_2+...+b_j - a_i<k$. Hỏi từng phần tử trong dãy $a$ có quan trọng hay không.

## Nhận xét 1:
Những phần tử $a_i\ge k$ luôn quan trọng. Bởi vì xét tập hợp $\{a_i\}$, ta có $a_i \ge k$, nhưng $a_i - a_i = 0 < k$.

## Nhận xét 2:
$(b_1+b_2+...+b_j \ge k)\ \&\&\ (b_1+b_2+...+b_j - a_i<k) \Leftrightarrow k - a_i \le b_1+b_2+...+b_j - a_i < k$.

## Ý tưởng đầu tiên:
Với mỗi phần tử của dãy $a$ mà $\le k$, kiểm tra xem phần tử đó có quan trọng hay không bằng cách quy hoạch động. Ký hiệu $dp[s] = true/false$ thể hiện việc có thể tạo được một tập hợp có giá trị $s$ hay không.
- $dp[0] = true$.
- với mỗi $j$ tăng dần từ $1$ đến $n$, $s$ giảm dần từ $n$ về $0$, $j \neq i$, $dp[s] = true$ $\Rightarrow$ $dp[s+a_j] = true$.
- Kiểm tra các giá trị $dp[j]$ với $k-a_i\le j < k$.

Độ phức tạp: $O(n^3)$, có thể tối ưu thành $O(\frac{n^3}{32})$ khi sử dụng bitset.

### Code: $(O(\frac{n^3}{32}),\ 91/100)$:

```cpp=1
#pragma GCC Optimize ("Ofast")
#include<iostream>
#include<bitset>
#include<vector>
#include<algorithm>
using namespace std;

vector<int>A, B, ind, ans;

int n, k;

bool check(long long d)
{
    if(d == A.size()) return true;
    if(d == -1) return false;
    bitset<10000> dp;
    dp.set(0);
    for(int i = 0; i < A.size(); i ++)
        if(i != d) dp |= (dp << (A[i]));
    for(int i = k - A[d]; i < k; i ++)
        if(dp.test(i)) return true;
    return false;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    freopen("SOLDIER.inp", "r", stdin);
    freopen("SOLDIER.out", "w", stdout);
    cin >> n >> k;
    int t;
    for(int i = 1; i <= n; i ++)
    {
        cin >> t;
        B.push_back(t);
        if(t >= k) ans.push_back(1);
        else
        {
            ans.push_back(0);
            A.push_back(t);
            ind.push_back(i);
        }
    }
    for(int i = 0; i < A.size(); i ++) if(check(i)) ans[ind[i] - 1] = 1;
    for(int i = 0; i < n; i ++) cout << ans[i];
}
```

## Nhận xét 3:
Nếu $a_i$ quan trọng mà $a_j \ge a_i$, chắc chắn $a_j$ quan trọng.

*Chứng minh:* Do $a_i$ quan trọng nên hiển nhiên tồn tại một tập $S$ sao cho tổng các phần tử trong tập $S$, ký hiệu là $d$, không nhỏ hơn $k$, đồng thời $d-a_i<k$. Xét các trường hợp sau:
- Trường hợp 1: $S$ không chứa $a_j$: lúc này ta thay $a_i$ trong $S$ thành $a_j$, được một tập $S'$ mới có tổng băng $d-a_i+a_j\ge d\ge k$. Sau khi loại bỏ $a_j$ khỏi tập $S'$, tập mới thu được có tổng bằng $d-a_i<k$. Vậy $a_j$ quan trọng.
- Trường hợp 2: $S$ chứa $a_j$: ta loại bỏ $a_j$ khỏi tập $S$ và thu được một tập mới có tổng bằng $d-a_j\le d-a_i <k$. Vậy $a_j$ cũng quan trọng.

Sau khi có nhận xét này, ta thấy rằng thay vì phải xét tất cả các số như ý tưởng đầu tiên, ta có thể chặt nhị phân.

Độ phức tạp: $O(n^2\times log_2(n))$, có thể tối ưu thành $O(\frac{n^2\times log_2(n)}{32})$ khi sử dụng bitset.

### Code $(O(\frac{n^2\times log_2(n)}{32}), \ 100/100)$:
(best submission tại thời điểm 10 giờ 48 phút sáng ngày 6/12/2022)
```cpp=1
#pragma GCC Optimize ("Ofast")
#include<iostream>
#include<bitset>
#include<vector>
#include<algorithm>
using namespace std;

vector<int>A, B, ind, ans;

int n, k;

bool cmp(int a, int b)
{
    return B[a - 1] < B[b - 1];
}

bool check(long long d)
{
    if(d == A.size()) return true;
    if(d == -1) return false;
    bitset<10000> dp;
    dp.set(0);
    for(int i = 0; i < A.size(); i ++)
        if(i != d) dp |= (dp << (A[i]));
    for(int i = k - A[d]; i < k; i ++)
        if(dp.test(i)) return true;
    return false;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    freopen("SOLDIER.inp", "r", stdin);
    freopen("SOLDIER.out", "w", stdout);
    cin >> n >> k;
    int t;
    for(int i = 1; i <= n; i ++)
    {
        cin >> t;
        B.push_back(t);
        if(t >= k) ans.push_back(1);
        else
        {
            ans.push_back(0);
            A.push_back(t);
            ind.push_back(i);
        }
    }
    sort(A.begin(), A.end());
    sort(ind.begin(), ind.end(), cmp);
    int l = 0, r = A.size(), m;
    while(l < r)
    {
        m = (l + r) / 2 + 1;
        if(check(m - 1) == false) l = m;
        else r = m - 1;
    }
    l --;
    for(int i = l + 1; i < A.size(); i ++) ans[ind[i] - 1] = 1;
    for(int i = 0; i < n; i ++) cout << ans[i];
}
```