AtCoder Solutions 1: Number Theory, Binary Search, and Representation
March-June 2024, primarily based on original solution records, including ABC 340 F, 341 D, 346 D/E/F, 356 D/E/F.
Scope of Coverage#
ABC 340 FABC 341 DABC 346 DABC 346 EABC 346 FABC 356 DABC 356 EABC 356 F
ABC 340 F - S = 1#
Given integers and (where are not both ).
Find a pair of integers that satisfies all the following conditions. If none exists, output -1.
- The area of the triangle with vertices at points , , and on the -plane is .
Solution#
It is easy to derive: The equation of the line formed by points and is:
Then the distance to is:
Thus the area of the triangle:
Let . (Note: is defined as the greatest common divisor of and .)
The extended Euclidean algorithm takes an integer pair as input and finds an integer pair such that , with time complexity . (Here, the integer pair is guaranteed to satisfy .)
By taking as input to the extended Euclidean algorithm, we can obtain a pair and find a feasible integer solution set for .
Multiply by , where (ensuring is an integer).
pair<ll, ll> extgcd(ll a, ll b) {
if (!b) return {1, 0};
ll x, y;
tie(y, x) = extgcd(b, a % b);
y -= a / b * x;
return {x, y};
}
void solve() {
ll x, y;cin >> x >> y;
if (abs(__gcd(x, y)) > 2) {
cout << "-1\n";return;
}
auto [c, d] = extgcd(y, -x);
c *= 2 / abs(__gcd(x, y)), d *= 2 / abs(__gcd(x, y));
cout << c << " " << d << '\n';
}cppABC 341 D - Only one of two#
Given (, ), array consists of multiples of and multiples of , excluding multiples of . Find .
#define int long long
void solve() {
int N, M, K;
cin >> N >> M >> K;
int low = 1, high = 1e18, ans = -1;
while (low <= high) {
int mid = (high + low) / 2;
// lcm(a,b)=(a / __gcd(a, b)) * b
int count = mid / N + mid / M - 2 * (mid / ((N / __gcd(N, M)) * M));
if (count >= K) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
cout << ans << '\n';
}cppSolution#
Binary Search
The count of numbers divisible by either or but not both is .
Official solution:
Let be the least common multiple of and .
Then, below a positive integer , the number of integers divisible by both and is . Therefore, between and , the number of integers divisible by either or but not both is .Moreover, since this count is monotonically increasing with respect to , the condition “the answer is required to be below ” is equivalent to “there are at least numbers between and that are divisible by either or but not both”, i.e., .
Therefore, this problem can be solved using binary search. Within the constraints of this problem, the answer will never exceed , so the search range can be set from to .
The proof that the answer is less than or equal to is as follows: Without loss of generality, assume . Let the greatest common divisor of and be , , (, , are integers). Then
When , under the problem constraints we have , , therefore
Thus, under the problem constraints, there are at least qualifying numbers below , and in particular at least qualifying numbers. Actually, under the current constraints, with , , , the answer is .
Specifically, first set , then as long as , repeat the following steps:
- Set .
- Determine if , if true, set , otherwise set .
The resulting is the answer.
For a fixed , the complexity of checking is , and the number of iterations is at most , making it very efficient. Therefore, this problem can be well solved using binary search.
ABC 346 D - Gomamayo Sequence#
You are given a string of length consisting of 0 and 1.
A string of length consisting of 0 and 1 is a good string if and only if it satisfies the following condition:
- There is exactly one integer such that and the -th and -th characters of are the same.
For each , you can choose whether to perform the following operation once:
- If the -th character of is “0”, replace it with “1”, and vice versa. If you perform this operation, the cost is .
Find the minimum total cost required to make a good string.
Solution#
Prefix-Suffix Sum
It can be seen that except for the positions which are the same, all other bits form an alternating 01 string. Consider two cases: and . Process the prefix and suffix separately. Then the answer at this point is or (respectively representing the cost required when bits are both 0 or both 1, and all other bits form an alternating 01 string). Take the minimum value.
#define int long long
void solve() {
int n;cin >> n;string s;cin >> s;vector<int> a(n + 1);s = ' ' + s;
for (int i = 1;i <= n;i++)cin >> a[i];
vector<array<int, 2>> f(n + 2), g(n + 2);
for (int i = 1;i <= n;i++) {
f[i][0] = f[i - 1][1] + (s[i] == '0' ? 0 : a[i]);
f[i][1] = f[i - 1][0] + (s[i] == '1' ? 0 : a[i]);
}
int ans = 1e18;
for (int i = n;i >= 2;i--) {
g[i][0] = g[i + 1][1] + (s[i] == '0' ? 0 : a[i]);
g[i][1] = g[i + 1][0] + (s[i] == '1' ? 0 : a[i]);
ans = min(ans, f[i - 1][0] + g[i][0]);
ans = min(ans, f[i - 1][1] + g[i][1]);
}
cout << ans << '\n';
}cppABC 346 E - Paint#
There is a grid with rows and columns. Initially, all cells are painted with color .
You will perform the following operations in order for .
- If , repaint all cells in the -th row with color .
- If , repaint all cells in the -th column with color .
After all operations are completed, for each color present in the grid, find the number of cells painted with color .
Solution#
Reverse Thinking
Traverse from back to front, which avoids complex discussions, and later operations are guaranteed to be final without worrying about being overwritten.
#define int long long
void solve() {
int n, m, q;cin >> n >> m >> q;
vector<int> t(q + 1), a(q + 1), x(q + 1), visn(n + 1), vism(m + 1);
for (int i = 1;i <= q;i++) {
cin >> t[i] >> a[i] >> x[i];
}
int n1 = n, m1 = m;
vector<int> cnt(2e5 + 1);
for (int i = q;i >= 1;i--) {
if (t[i] == 1) {
if (!visn[a[i]]) {
n1--;
visn[a[i]] = 1;
cnt[x[i]] += m1;
}
} else {
if (!vism[a[i]]) {
m1--;
vism[a[i]] = 1;
cnt[x[i]] += n1;
}
}
}
cnt[0] += n1 * m1;
vector<pair<int, int>> ans;
for (int i = 0;i <= 2e5;i++) {
if (cnt[i]) {
ans.push_back({i,cnt[i]});
}
}
cout << ans.size() << '\n';
for (auto [x, y] : ans)cout << x << " " << y << '\n';
}cppABC 346 F - SSttrriinngg in StringString#
For a string of length , let denote the string obtained by repeating the string times, and denote the string obtained by repeating the first character, second character, …, up to the -th character of times each.
For example, if abc, then abcabc, aaabbbccc. Also, for any string , and are both empty strings.
Given a positive integer and strings and . Find the largest non-negative integer such that is a (not necessarily contiguous) subsequence of .
is always a subsequence of .
Solution#
Binary Search + Greedy
Official Solution
Fix and consider how to determine whether is a subsequence of .
Let be the length of , be the length of , and denote the first characters of string . Each character in must also appear in (otherwise the answer is clearly 0).
Let us sequentially find the following values in the order of :
- : the smallest such that is a subsequence of .
Finally, by comparing with , we obtain the answer. Finding from is equivalent to finding the position of the -th occurrence of the -th character of after the ()-th character (inclusive) of . Therefore, this problem reduces to handling the following query times:
- : Given positive integers , , and a character , find the position of the -th occurrence of character after the -th character (inclusive) of .
Assume appears times in . If is greater than , reduce it to the case by . If is greater than , reduce it to the case by .
Therefore, we only need to handle queries satisfying , . If these conditions are satisfied, the answer to is no greater than . By precomputing for each character its occurrence positions in , queries can be handled quickly.
Thus, including the initial binary search for , the problem can be solved in a total time of or (where is the size of the alphabet).
During implementation, the positions of each character in S + S are preprocessed, so each query(a,b,c) can locate the answer after a constant number of jumps; the binary search decision is then grounded accordingly.
represents the count of each letter in the first characters of the string.
The reason for is that when it’s exactly divisible, it would carry over to the next digit, and at that point would record the position of the corresponding character in the next cycle. However, we actually need to record the last position of that letter in the previous cycle.
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 200005;
char s[N], t[N];
int a[N][26];
vector<int> b[26];
int main() {
LL n;
scanf("%lld%s%s", &n, s + 1, t + 1);
int m = strlen(s + 1);
for (int i = 1; i <= m; i++) {
for (int j = 0; j < 26; j++) a[i][j] = a[i - 1][j];
a[i][s[i] - 'a']++;
b[s[i] - 'a'].push_back(i);
}
LL l = 1, r = 1e17 + 500;
while (l < r) {
LL mid = l + r >> 1;
LL k = 0, z = 0;
int ok = 0;
for (int i = 1; t[i] && k < n; i++) {
int c = a[m][t[i] - 'a'];// The count of this letter in the s string <-> int c = b[t[i] - 'a'].size();
if (c == 0) {// If this character does not exist, end immediately (this mid as an answer does not meet the requirement, the answer should be smaller)
k = n;break;
}
LL x = a[z][t[i] - 'a'] + mid;
k += (x - 1) / c;
x = (x - 1) % c + 1;
z = b[t[i] - 'a'][x - 1];// Record the subscript in the last cycle that meets the condition
}
if (k >= n)
// If the number of cycles has already exceeded n before finishing traversing the t string, it proves that the requirement cannot be met. The goal of this binary search is to find the first index where k >= n
r = mid;
else
l = mid + 1;
}
printf("%lld\n", l - 1);
return 0;
}cppABC 356 D - Masked Popcount#
Given integers and , compute the sum modulo .
Bitwise operation: Based on the property of , increases only when both bits are 1, so we only need to enumerate the bits where is 1.
In the interval , if we look at the -th bit, we find that it alternates with zeros and ones (i.e., the period is ). We can first calculate how many periods covers, and then how many ones are covered in the last incomplete period.
It’s easy to calculate: For the -th bit: .
is used because when calculating the count here, 0 is included, so one less is counted later, which needs to be added back.
#define int long long
constexpr int mod = 998244353;
void solve() {
int n, m;cin >> n >> m;
int sum = 0;
n++;
for (int i = 0;i <= __lg(m);i++) {
if ((m >> i) & 1) {
int x = n / (1ll << (i + 1));
int y = n % (1ll << (i + 1));
sum = (sum + x * (1ll << i) + max(y - (1ll << i), 0ll)) % mod;
}
}
cout << sum << '\n';
}cppABC 356 E - Max/Min#
Given array , compute:
When the count of identical numbers is , the contribution is .
When two numbers are different, for , when , the contribution is .
First, use an array to represent how many times appears in array . Use to represent the prefix sum of . represents the count of numbers in the interval .
For each , enumerate multiples of , look at the interval , i.e., . For this case: .
jiangly:
void solve() {
int n;cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
const int M = *max_element(a.begin(), a.end());
vector<int> c(M + 1);
for (auto x : a) {
c[x]++;
}
vector<int> s(M + 1);
for (int i = 1; i <= M; i++) {
s[i] = s[i - 1] + c[i];
}
int ans = 0;
for (int x = 1; x <= M; x++) {
ans += c[x] * (c[x] - 1) / 2;
for (int y = x; y <= M; y += x) {
int v = s[min(y + x - 1, M)] - s[max(x, y - 1)];
ans += c[x] * v * (y / x);
}
}
cout << ans << "\n";
}cppABC 356 F - Distance Component Size Query#
You are given an integer . For an initially empty set , process the following queries of two types in order:
1 x: Given an integer . If is in , remove from . Otherwise, add to .2 x: Given an integer that is in . Consider a graph whose vertices are the numbers in , and an edge exists between two numbers if and only if their absolute difference is at most . Print the number of vertices in the connected component containing .
Balanced tree from _pbds
The main idea is to maintain two sets: one records the current set of elements, and the other records the set after compression.
Compression means compressing each connected block into the largest number in that block.
Thus, when querying the size of the connected component containing , it is the position of this connected block in the first set minus the position of the previous connected block in the first set.
ordered_set<int> s1;set<int> s2; // s1 is the set of all points; s2 compresses each connected block into its largest point
// (s1 requires rank, so PBDS must be used; s2 can be an ordinary set)
void solve() {
ll q, k;cin >> q >> k;
// Inserting a few sentinels here is for convenience in problem-solving, so prev and next on boundaries can be unified
s1.insert(-4e18), s1.insert(4e18);
s2.insert(-4e18), s2.insert(4e18);
while (q--) {
ll op, x;cin >> op >> x;
if (op == 1) {
if (s1.find(x) == s1.end()) {
auto it = s1.insert(x).first;
ll w1 = *prev(it), w2 = *next(it); // Numbers adjacent to x on left and right
if (x - w1 <= k) s2.erase(w1);
if (w2 - x <= k)
; // No need to insert x into s2
else
s2.insert(x);
} else {
auto it = s1.find(x);
ll w1 = *prev(it), w2 = *next(it); // Numbers adjacent to x on left and right
s1.erase(x), s2.erase(x); // It doesn't matter if x wasn't in s2
if (w2 - w1 > k) s2.insert(w1); // Let w1 revive (it doesn't matter if it was already active)
}
} else {
auto it = s2.lower_bound(x); // Find the largest point in the connected block containing x
cout << s1.order_of_key(*it) - s1.order_of_key(*prev(it)) << '\n';
}
}
}cpp