Lời Giải HSG Tin Tỉnh Quảng Ninh (B) - 2019

November 4, 2025 August 16, 2024
Author Author Hung Nguyen Tuong

Bài 1. Tổng các ước

Có hai số nguyên dương x,yx, y.

Yêu cầu

Bạn hãy viết chương trình tính tổng các ước nguyên dương của từng số và tìm tổng lớn hơn trong hai tổng đó, nếu hai tổng có giá trị bằng nhau thì tổng lớn hơn là giá trị bằng nhau đó.

Dữ liệu

Vào từ tệp TONGUOC.INP gồm một dòng ghi hai số nguyên dương x,yx,y viết cách nhau một dấu cách (1x,y1012)(1 \le x,y \le 10^{12}).

Kết quả

Ghi vào tệp TONGUOC.OUT trên một dòng, một số nguyên dương theo yêu cầu bài toán.

Ví dụ

TONGUOC.INPTONGUOC.OUT
10 1118
12 1228

Ràng buộc

  • 50%50\% số test ứng với 50%50\% số điểm của bài có: x,y103x,y \le 10^3.
  • 30%30\% số test ứng với 30%30\% số điểm của bài có: x,y107x,y \le 10^7.
  • 20%20\% số test ứng với 20%20\% số điểm của bài có: x,y1012x,y \le 10^{12}.

Lời giải

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define nl '\n'
ll sumdiv(ll n){
    if (n==1) return 1;
    ll ans = 1 + n;
    for (ll i=2;i*i<=n;i++){
        if (n%i==0){
            if (i*i==n) ans += i ;
            else ans += i + n/i;
        }
    }
    return ans;
}
int main()
{
    ll x, y;
    cin >> x >> y;
    cout << max(sumdiv(x), sumdiv(y));
    return 0;
}

Bài 2. Xâu

Cho số nguyên dương NN và xâu SS gồm các kí tự là chữ cái (a,b,...,z,A,B,...,Z)(a,b,...,z,A,B,...,Z) viết liền nhau.

Chuẩn hoá xâu SS bằng cách chuyển các kí tự chữ cái thường thành các kí tự chữ cái in hoa. Ta quy ước kí tự A'A' xuất hiện đầu tiên trong xâu SS có giá trị là 11, kí tự A'A' tiếp theo xuất hiện trong xâu SS có giá trị là 2, kí tự A'A' tiếp theo xuất hiện trong xâu SS có giá trị là 3, cứ như thế cho đến cuối xâu SS. Đối với các kí tự khác cũng quy ước tương tự.

Sau đó tính tổng các giá trị đã được quy ước của xâu SS.

Ví dụ: nếu S=cAbbaCAaBS='cAbbaCAaB' thì chuẩn hoá S=CABBACAABS='CABBACAAB' và quy ước:

  • Kí tự A'A' xuất hiện 44 lần nên có các giá trị lần lượt là 1,2,3,41,2,3,4.
  • Kí tự B'B' xuất hiện 33 lần nên có các giá trị lần lượt là 1,2,31,2,3.
  • Kí tự C'C' xuất hiện 22 lần nên có các giá trị lần lượt là 1,21,2.

⇒ Tổng các giá trị xuất hiện sẽ là 1919.

Yêu cầu

Hãy viết chương trình cho biết tổng các giá trị theo quy ước như trên có chia hết cho số nguyên dương NN hay không. Nếu chia hết thì ghi ra tổng đó, nếu không chia hết thì ghi ra số lượng các chữ cái khác nhau của xâu SS sau khi chuẩn hoá.

Dữ liệu

Vào từ tệp XAU.INP gồm 2 dòng:

  • Dòng 1 chứa số nguyên dương N (1N106)N \ (1 \le N \le 10^6).
  • Dòng 2 chứa xâu SS (không quá 100000100000 kí tự).

Kết quả

Ghi ra tệp XAU.OUT trên một dòng, một số tự nhiên thoả mãn yêu cầu của bài toán.

Ví dụ

XAU.INPXAU.OUTGiải thích kết quả
5
AbaCcA
10Sau khi chuẩn hoá, tổng theo quy ước là 10 và chia hết cho 5. Ghi ra 10.
6
gZtAgG
4Sau khi chuẩn hoá, tổng theo quy ước là 9 và không chia hết cho 6. Ghi ra 4 là số kí tự chữ cái khác nhau của xâu S.

Ràng buộc

  • 40%40\% số test ứng với 40%40\% số điểm của bài với xâu SS chỉ xuất hiện ít nhất trong một các kí tự a,b,ca,b,c hoặc A,B,CA,B,C và độ dài xâu SS tối đa là 10001000 kí tự.
  • 40%40\% số test ứng với 40%40\% điểm của bài với xâu SS có độ dài tối đa 10001000 kí tự.
  • 20%20\% số test ứng với 20%20\% điểm của bài với xâu SS có độ dài tối đa 100000100000 kí tự.

Lời giải

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

int main()
{
    ll n, ans = 0; cin >> n;
    string s; cin >> s;
    map<char,int> mp;
    for (auto i : s){
        if ('a' <= i && i <= 'z'){
            ans += ++mp[i-'a'];
        }
        else{
            ans += ++mp[i-'A'];
        }
    }
    if (ans%n==0) cout << ans;
    else cout << mp.size();
    return 0;
}

Bài 3. Dãy số

Cho dãy số AANN số nguyên a1,a2,...,ana_1,a_2,...,a_n. Một dãy con liên tiếp các số hạng của dãy AA là dãy các số hạng tử số hạng aia_i đến số hạng aj (1ijN)a_j \ (1 \le i \le j \le N).

Yêu cầu

Hãy cho biết dãy AA có bao nhiêu dãy con liên tiếp mà giá trị tuyệt đối của tổng các số hạng trong dãy con đó lớn hơn một số nguyên dương SS cho trước.

Dữ liệu

Vào từ tệp DAYSO.INP gồm 2 dòng:

  • Dòng thứ nhất chứa hai số nguyên NNS (N105, S1014)S \ (N \le 10^5, \ S \le 10^{14}).
  • Dòng thứ hai chứa NN số nguyên a1,a2,...,aN (ai109)a_1,a_2,...,a_N \ (|a_i| \le 10^9).

Các số trên cùng một dòng được ghi cách nhau ít nhất một dấu cách.

Kết quả

Ghi ra tệp DAYSO.OUT trên một dòng, một số nguyên duy nhất là số dãy con liên tiếp thoả mãn yêu cầu của bài toán.

Ví dụ

DAYSO.INPDAYSO.OUTGiải thích kết quả
5 10
-5 5 6 2 6
5Các dãy con liên tiếp có giá trị tuyệt đối của tổng lớn hơn 10 là: {5,6}, {5,6,2}, {5,6,2,6}, {-5,5,6,2,6}.
4 4
5 -1 8 -5
6Các dãy con liên tiếp có giá trị tuyệt đối của tổng lớn hơn 4 là: {5}, {8}, {-1,8}, {5,-1,8}, {5,-1,8,5}.

Ràng buộc

  • 30%30\% số test ứng với 30%30\% số điểm của bài có N100N \le 100.
  • 50%50\% số test ứng với 50%50\% số điểm của bài có N103N \le 10^3.
  • 20%20\% số test ứng với 20%20\% số điểm của bài có N105N \le 10^5.

Lời giải

Ta sử dụng mảng cộng dồng pref\text{pref}, với pref[i]\text{pref}[i] là tổng cộng dồn từ đầu mảng đến vị trí ii.

⇒ Tổng phần tử từ iji \to j sẽ là pref[j]pref[i1]\text{pref}[j] - \text{pref}[i-1].

Theo yêu cầu của bài, cần tìm số đoạn (i,j)(i,j) sao cho:

pref[j]pref[i1]>S>0 |\text{pref}[j] - \text{pref}[i-1]| > S > 0

Tương đương với 1 trong 2 trường hợp sau:

  • pref[j]pref[i1]>S\text{pref}[j] - \text{pref}[i-1] > S.
  • Hoặc pref[j]pref[i1]<S\text{pref}[j]-\text{pref}[i-1] < -S.

Giả sử ta có 22 vị trí uuvv sao cho pref[v]pref[u]=K>S>0|\text{pref}[v]-\text{pref}[u]| = K > S > 0.

  • Nếu u<vu<v (tổng tuyệt đối của đoạn (u+1,v)(u+1,v) KK) và:
    • Nếu pref[v]>pref[u]\text{pref}[v] > \text{pref}[u] thì pref[v]pref[u]=Kpref[v]=pref[u]+K\text{pref}[v] - \text{pref}[u] = K \Rightarrow \text{pref}[v] = \text{pref}[u] + K.
    • Nếu pref[v]<pref[u]\text{pref}[v] < \text{pref}[u] thì pref[v]pref[u]=Kpref[u]=pref[v]+K\text{pref}[v] - \text{pref}[u] = -K \Rightarrow \text{pref}[u] = \text{pref}[v] + K.
  • Tương tự, nếu v<uv < u (tổng tuyệt đối của đoạn (v+1,u)(v+1,u) KK) và:
    • Nếu pref[u]>pref[v]\text{pref}[u] > \text{pref}[v] thì pref[u]pref[v]=Kpref[u]=pref[v]+K\text{pref}[u] - \text{pref}[v] = K \Rightarrow \text{pref}[u] = \text{pref}[v] + K.
    • Nếu pref[v]<pref[u]\text{pref}[v] < \text{pref}[u] thì pref[u]pref[v]=Kpref[v]=pref[u]+K\text{pref}[u] - \text{pref}[v] = -K \Rightarrow \text{pref}[v] = \text{pref}[u] + K.

Ta nhận thấy rằng uu ở trước vv hay vv ở trước uu đều không quan trọng.

Nghĩa là với mỗi uu bất kỳ, ta chỉ cần tìm vv sao cho pref[v]=pref[u]+K\text{pref}[v] = \text{pref}[u] + K. Cũng như với mỗi vv bất kỳ, ta chỉ cần tìm uu sao cho pref[u]=pref[v]+K\text{pref}[u] = \text{pref}[v] + K với K>SK > S.

Từ đó, ta sẽ có ý tưởng sắp xếp mảng pref\text{pref} theo thứ tự không giảm.

Đến đây, với mỗi vị trí uu, ta tìm phần tử đầu tiên mà >pref[v]+S> \text{pref}[v] + S, dẫn đến là các phần tử ở sau cũng sẽ >pref[v]+S> \text{pref}[v] + S. Vậy ta có được số vị trí vv tương từng với từng vị trí uu.

Độ phức tạp: O(N×logN)O(N \times \log N).

Code

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

int main()
{
    int n; cin >> n;
    ll s; cin >> s;
    ll pref[n+1], ans = 0;
    pref[0] = 0;
    for (int i=1;i<=n;i++){
        ll a; cin >> a;
        pref[i] = pref[i-1] + a;
    }
    sort(pref, pref + n + 1);
    for (int i=0;i<=n;i++){
        ans += n-(upper_bound(pref, pref + n + 1, pref[i] + s)-pref)+1;
    }
    cout << ans;
    return 0;
}