Lời Giải HSG Tin Tỉnh Quảng Ninh (B) - 2017
Bài 1. Thống kê chữ cái
Cho một xâu chỉ gồm các chữ cái tiếng Anh .
Yêu cầu
Bạn hãy thống kê các chữ cái xuất hiện trong xâu theo thứ tự từ điển và số lần xuất hiện của nó. Việc thống kê các chữ cái không phân biệt chữ hoa, chữ thường.
Dữ liệu
Tệp VANBAN.INP chứa một dòng ghi xâu .
Kết quả
Ghi vào tệp VANBAN.OUT với nội dung:
- Dòng đầu tiên ghi số lượng chữ cái khác nhau có trong xâu (không phân biệt chữ hoa, chữ thường).
- Mỗi dòng tiếp theo ghi một chữ cái in hoa, tiếp theo là một dấu cách và số lần xuất hiện của nó trong xâu .
Ví dụ
| VANBAN.INP | VANBAN.OUT |
|---|---|
| aFbAAffFbbDda | 4 A 4 B 3 D 2 F 4 |
Ràng buộc
- 30% số test ứng với 30% số điểm của bài có độ dài xâu không lớn hơn .
- 30% số test ứng với 30% số điểm của bài có độ dài xâu không lớn hơn .
- 40% số test ứng với 40% số điểm của bài có độ dài xâu không lớn hơn .
Lời giải
#include <bits/stdc++.h>
using namespace std;
#define nl '\n'
int main()
{
string s;
cin >> s;
map<char, int> f;
for (char c : s)
f[('a' <= c && c <= 'z') ? c - 32 : c]++;
cout << f.size() << nl;
for (auto &p : f)
cout << p.first << " " << p.second << nl;
return 0;
}Bài 2. Đoạn thẳng dài nhất
Trên trục số, người ta tô đậm ba đoạn , và với các điểm đầu và điểm cuối đều có tọa độ nguyên.
Yêu cầu
Tìm đoạn có độ dài lớn nhất được tô đậm trên trục số từ ba đoạn đã cho.
Dữ liệu
Tệp DOAN.INP gồm 3 dòng, dòng thứ chứa 2 số nguyên , với , cách nhau bởi dấu cách.
Kết quả
Ghi vào tệp DOAN.OUT một số nguyên là độ dài của đoạn được tô đậm dài nhất trên trục số.
Ví dụ
| DOAN.INP | DOAN.OUT |
|---|---|
| 1 4 -2 2 5 10 | 6 |
| -2 0 0 3 5 9 | 5 |
| 0 1 2 4 -12 -5 | 7 |
Ràng buộc
- 30% số test ứng với 30% số điểm trong trường hợp không có hai đoạn nào có điểm chung.
- 30% số test ứng với 30% số điểm trong trường hợp .
- 40% số test ứng với 40% số điểm trong trường hợp .
Lời giải
Trước tiên, đọc ba đoạn và sắp xếp theo điểm bắt đầu.
Duyệt qua các đoạn. Nếu một đoạn mới giao hoặc chạm với đoạn hiện tại, ta mở rộng đoạn hiện tại. Nếu không, ta chuyển sang đoạn mới.
Trong quá trình này, độ dài lớn nhất của các đoạn liên tục được cập nhật.
Cuối cùng, đưa ra kết quả là đoạn dài nhất được tô đậm.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
vector<pair<ll, ll>> s(3);
for (int i = 0; i < 3; i++)
cin >> s[i].first >> s[i].second;
sort(s.begin(), s.end());
pair<ll, ll> cur = s[0];
ll ans = cur.second - cur.first;
for (int i = 1; i < 3; i++)
{
if (s[i].first <= cur.second)
cur.second = max(cur.second, s[i].second);
else
cur = s[i];
ans = max(ans, cur.second - cur.first);
}
cout << ans;
return 0;
}Bài 3. Tổng các chữ số
Yêu cầu
Bạn hãy đếm số lượng trong đoạn có tổng các chữ số bằng cho trước.
Ví dụ thì có số có tổng các chữ số bằng đó là .
Dữ liệu
Vào từ tệp CHUSO.INP gồm một dòng chứa 3 số nguyên viết cách nhau bởi dấu cách .
Kết quả
Ghi ra tệp CHUSO.OUT gồm số tự nhiên duy nhất là số lượng các số có tổng các chữ số bằng nằm trong đoạn .
Ví dụ
| CHUSO.INP | CHUSO.OUT |
|---|---|
| 15 100 10 | 9 |
Ràng buộc
- Có số test ứng với số điểm của bài có .
- Có số test ứng với số điểm của bài có .
- Có số test khác ứng với số điểm của bài có .
Lời giải
Vì có thể rất lớn, nên việc tính tổng chữ số từng số từ là không khả thi.
Chúng ta sẽ áp dụng quy hoạch động trên chữ số (Digit DP).
Tóm tắt đề bài, ta cần tìm số lượng số sao cho:
- .
- Tổng các chữ số của là .
Giả sử là số lượng số mà tổng các chữ số là , ký hiệu .
Vậy số lượng số trong đề bài cần tìm là:
Vậy làm để có được ?
Với thì việc tính quá đơn giản.
Trường hợp , khi có nhiều chữ số, ta có thể tách thành 2 phần như sau:
- Chữ số cuối cùng: .
- Phần còn lại: .
Ví dụ: .
Và mỗi số cũng biểu diễn được như vậy:
- Với là chữ số cuối .
- là phần còn lại, có số chữ số số chữ số của .
Ta thấy rằng, vì nên sẽ luôn đảm bảo . Nhưng khi ta ghép thêm vào để so sánh với , điều ngược lại chưa chắc đúng. Bởi 2 trường hợp có thể xảy ra:
- : Nếu thì chắc chắn , nếu , hàng đơn vị của vẫn của nên .
- : Nếu thì vẫn đảm bảo , nhưng nếu , hàng đơn vị của của nên .
Biết rằng:
Để thì phải là .
Vậy số lượng số thoả mãn cũng là số lượng số thoả mãn với mỗi :
- Thêm vào cuối được . Theo quan sát ở trên, ta có .
- .
Ta suy ra được hàm đệ quy như sau:
Và ta cũng có các trường hợp cơ sở sau:
- nếu hoặc .
- nếu .
#include <bits/stdc++.h>
using namespace std;
map<pair<int, int>, int> memo;
int T(int k, int s)
{
if (k < 0 || s < 0)
return 0;
if (!k)
return !s;
if (memo.find({k, s}) != memo.end())
return memo[{k, s}];
int ans = 0;
for (int i = 0; i <= 9; i++)
ans += T(k / 10 - (i > (k % 10)), s - i);
return memo[{k, s}] = ans;
}
int main()
{
int L, R, h;
cin >> L >> R >> h;
cout << T(R, h) - T(L - 1, h);
return 0;
}Vì mỗi lần gọi đệ quy ta rút gọn đi một chữ số. Giả sử ban đầu có chữ số , nên chỉ rút gọn đi lần. được giảm từ xuống , qua mỗi lần đệ quy trừ đi , nên số các giá trị tối đa có thể có là . Vậy ta có trạng thái, mỗi trạng thái duyệt qua giá trị là , coi là vì là hằng số.
Nhờ lưu kết quả trong memo, nên độ phức tạp của thuật toán là .