Lời Giải Khảo Sát Đội Tin HQV Đợt 2 2024

November 4, 2025 October 31, 2024
Author Author Hung Nguyen Tuong

Bài 1. EVEN

image.png

Lời giải

Ta sẽ đếm số số lẻ từ 1n1 \to n, và số số chẵn từ 1n1 \to n.

Rồi áp dụng công thức sau:

  • Tổng kk số lẻ đầu tiên từ 11:

    k2 k^2
  • Tổng kk số chẵn đầu tiên từ 11:

    k×(k+1) k\times(k+1)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define nl '\n'

ll solve(ll n)
{
    ll even, odd;
    if (n % 2 == 0)
    {
        even = odd = n / 2;
    }
    else
    {
        even = n / 2;
        odd = even + 1;
    }
    return max(odd * odd, even * (even + 1));
}

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        ll n;
        cin >> n;
        cout << solve(n) << nl;
    }
    return 0;
}

Bài 2. SUM

image.png

image.png

Lời giải

Giả sử ban đầu mọi phần tử trong mảng nhân với 11:

image.png

Nếu chọn vị trí jj bất kỳ và nhân các phần tử từ vị trí 1j1 \to j với 1-1 thì ta được:

image.png

hoặc

image.png

Tương tự với kk bất kỳ từ nkn \to k:

image.png

hoặc

image.png

Nhưng nếu ta thực hiện cả 2 thao tác thì luôn có một đoạn con ở giữa sẽ vẫn nhân với 11 như ban đầu:

Trường hợp j<kj < k:

image.png

Trường hợp j=kj=k:

image.png

Trường hợp j>kj > k:

image.png

Từ các quan sát như vậy ta kết luận được các trường hợp chính như sau:

  1. Toàn bộ là ++.
  2. Toàn bộ là -.
  3. Một đoạn ++ ở giữa 2 đoạn -.
  4. Mảng chia thành 2 phần +,+,-.

Vậy từ đây ta có được ý tưởng như sau, kết quả của bài toán chính là giá trị lớn nhất của:

  • Tổng của toàn bộ mảng nhân với 11.
  • Tổng của toàn bộ mảng nhân với 1-1.
  • 22 lần tổng lớn nhất của 1 đoạn con liên tiếp trong dãy ban đầu + tổng toàn bộ mảng nhân với 1-1.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define nl '\n'

int main()
{
    int n, a;
    cin >> n;
    int total = 0, current = 0, maxSum = 0;
    for (int i = 0; i < n; i++)
    {
        cin >> a;
        total += a;
        current = max(a, current + a);
        maxSum = max(maxSum, current);
    }
    cout << 2 * maxSum - total;
    return 0;
}

Bài 3. STRING

image.png

Lời giải

Vì chỉ có 2626 ký tự chữ cái Latinh, nên mức độ của chuỗi có giá trị nằm trong khoảng [1,26][1,26].

Ý tưởng như sau:

Ta sẽ tính mức độ của tất cả các chuỗi con kết thúc tại vị trí r (0r<n)r \ (0 \le r < n). Và sau đó tổng hợp lại các kết quả ấy.

Ví dụ có chuỗi s="cabdccbcaa"s = "cabdccbcaa".

Giả sử ta cần đếm mức độ của tất cả các chuỗi con kết thúc tại vị trí cuối cùng r=9r=9:

image.png

Ta thấy rằng số chuỗi con s[l,9]s[l,9] có mức độ:

  • 11 khi 7<l97 < l \le 9.
  • 22 khi 6<l76 < l \le 7.
  • 33 khi 3<l63 < l \le 6.
  • 44 khi 1<l3-1 < l \le 3.

Với 77 là vị trí xuất hiện cuối cùng của cc, 66 là vị trí xuất hiện cuối cùng của bb, 33 là vị trí xuất hiện cuối cùng của dd, 1-1 là toàn bộ chuỗi con kết thúc tại rr đều có mức độ là 44.

Từ đây ta có ý tưởng, với mỗi ký tự có trong chuỗi ss khi xét đến rr, ta sẽ lưu lại vị trí xuất hiện cuối cùng của các ký tự đó vào mảng pp.

image.png

Và sau đó thêm 1 phần tử 1-1, sắp xếp mảng pp để tạo ra mảng xx như sau:

image.png

Vậy ở ví dụ này, số chuỗi con có mức độ kk sẽ là:

ans[k]=x[4k+1]x[4k] ans[k] = x[4-k+1]-x[4-k]

Trong trường hợp tổng quát hơn ta có 2626 chữ cái Latinh phân biệt, lúc đó mảng xx sẽ có 2727 phần tử [026][0\ldots26]:

ans[k]=x[26k+1]x[26k]Let j=26k+1 (1j26)k=26j+1ans[27j]=x[j]x[j1] ans[k] = x[26-k+1] - x[26-k]\\ \text{Let} \ j = 26-k+1 \ (1 \le j \le 26) \Rightarrow k = 26-j+1 \\ \Rightarrow ans[27-j] = x[j]-x[j-1]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define nl '\n'

int main()
{
    string s;
    cin >> s;
    vector<int> p(26, -1);
    vector<ll> ans(26, 0);
    for (int i = 0; i < s.size(); i++)
    {
        p[s[i] - 'a'] = i;
        vector<int> x = p;
        x.push_back(-1);
        sort(x.begin(), x.end());
        for (int j = 1; j <= 26; j++)
            ans[26 - j] += x[j] - x[j - 1];
    }

    cout << find(ans.begin(), ans.end(), 0) - ans.begin() << nl;
    for (int i = 0; i < ans.size() && ans[i] != 0; i++)
        cout << ans[i] << nl;

    return 0;
}

Bài 4. DISTANCE

image.png

Lời giải

Như đề bài đã cho, đầu vào là 1 cây, nên đây là một đồ thị liên thông, không có chu trình với NN đỉnh.

Vì thế đường đi giữa 2 node bất kỳ là duy nhất.

Ta có thể suy ngay được ra là đường đi xa nhất giữa 2 node trong cây sẽ có 2 đầu là 2 node lá. Bởi nếu 1 trong 2 node đấy không phải lá thì có đường đi đó vẫn có thể tiếp tục đi đến node con và tạo ra đường đi xa hơn.

Ta có ý tưởng như sau:

  1. DFS từ node gốc cây, để tìm ra node lá xa nhất và lưu lại nốt lá đó gọi là aa.
  2. Rồi lại DFS từ node lá aa (giờ ta coi như aa là một node gốc) để tìm ra node lá xa nhất gọi là bb.

Vậy khoảng cách xa nhất giữa 2 đỉnh bất kỳ trên cây chính là khoảng cách giữa aabb.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define nl '\n'

const int mx = 2e5 + 2;
vector<vector<int>> adj(mx, vector<int>());
vector<bool> visit(mx);

int fv = -1, md = -1;

void dfs(int u, int d)
{
    visit[u] = true;

    if (d > md)
    {
        md = d;
        fv = u;
    }

    for (auto v : adj[u])
    {
        if (!visit[v])
            dfs(v, d + 1);
    }
}

int main()
{
    int n;
    cin >> n;
    int root = 0;
    for (int i = 1; i <= n; i++)
    {
        int p;
        cin >> p;
        adj[i].push_back(p);
        adj[p].push_back(i);
        if (!p)
            root = i;
    }

    visit.assign(mx, false);
    dfs(root, 0);

    visit.assign(mx, false);
    md = -1;
    dfs(fv, 0);

    cout << md;
    return 0;
}