You are on page 1of 7

Bangladesh Olympiad in Informatics 2019

Divisional Round Analysis


March 12, 2019

1 Welcome to BdOI 2019


Setter. Anik Sarker, BUET Tester. You

Abridged Statement. You have to print the smallest two digit positive integer that is
divisible by 3.

Solution. This was a giveaway problem. The number is 12.

Sample Code. O (1)


#include <stdio.h>

int main() {
puts("12");
return 0;
}

2 Help Caesar
Setter. Anik Sarker, BUET Tester. Sahed Sohel, Google

Abridged Statement. Find the maximum number of occurrences of a given string T


in the given string S such that no two of the occurrences overlap.

Solution. A simple greedy approach works: keep picking the earliest occurrence that does
not overlap any of the previously picked ones. That is: pick the first occurrence, then the
next occurrence that doesn’t overlap with the first one, then the next one that doesn’t over-
lap with the first two, and so on. We leave it to you to figure out why this would work.
Doing it naively gives us an O (|S||T |) solution, which was fast enough under the constraints
given in the problem. But using a fast string matching algorithm (KMP for example) we
can turn it into an O (|S| + |T |) solution.

1
Sample Code. O (|S||T |)
#include <bits/stdc++.h>

using namespace std;

int main() {
string S, T;
cin >> S >> T;
int ans = 0;
int last = INT_MIN;
for (int i = 0; i < S.size(); ++i) {
string P = S.substr(i, T.size());
if (P == T && i - last >= T.size()) ++ans, last = i;
}
cout << ans << endl;
return 0;
}

3 Constrained Points
Setter. Jubayer Nirjhor, DU Tester. Sahed Sohel, Google

Abridged Statement. Choose n distinct points in the plane and color each of them
in white or black so that each circle of radius r centered at a white point contains exactly
two black points on its circumference and the number of white points is maximized. Print
the number of maximum white points.

Solution. Suppose there are w white points and b black points, so w + b = n. Now
each pair of black points can be on the
 circumference of at most 2 circles of radius r
b
(why?). So we get the bound w ≤ 2 = b2 − b so that b2 ≥ w + b = n implying
2
b ≥ dne. So we have w = n − b ≤ n − dne. This bound can be achieved by plac-
ing dne black points on the vertices of a regular polygon with side length less than 2r
and extracting the white points accordingly. So the answer is simply n − dne. See be-
low for an example on 9 points where A, B, C are the black points, the others are white.

2
Sample Code. O (1)
#include <bits/stdc++.h>

using namespace std;

int main() {
long long n, r;
cin >> n >> r;
long long ans = n - ceil(sqrtl(n));
cout << ans << endl;
return 0;
}

3
4 Mafia
Setter. Saad Muhammed Junayed, BUET Tester. Sakibul Mowla, Bloomberg

Abridged Statement. There are n persons in a row, k of them are mafias and they
know a secret. Each second, every person knowing the secret passes it on to his neighbors
(one on each side). You can choose and block m persons, where blocking a person means
he’ll never know the secret (and hence won’t pass it on either). Minimize the number of
people the secret will reach.

Solution. Let’s consider the segments of people between two consecutive mafias (k − 1
such segments). Inside a segment: if we block 1 person we can save only that person; if we
block 2 persons, we can save the whole segment by blocking the 2 persons at the endpoints.
So it’s better to save such segments in decreasing order of their lengths. However, we have
two special segments: a prefix segment and a suffix segment. The whole of them can be
saved by blocking 1 person each. But this is easy to fix: we can just consider 4 cases.

• save both the prefix and the suffix segment and save internal segments using m − 2
blocks

• save only the prefix segment and save internal segments using m − 1 blocks

• save only the suffix segment and save internal segments using m − 1 blocks

• save only internal segments using m blocks

We need to sort the segment lengths in O (k lg k) and run a simulation for internal segments
in O(k) for each case. So the overall solution works in O (k lg k).

Sample Code. O (T k lg k)
#include <bits/stdc++.h>

using namespace std;

const int N = 10010;


const int INF = 1e7 + 5;

int t, n, m, k, a[N], s[N];

int calc (int b) {


if (b < 0) return -INF;
if (b == 0) return 0;
int ret = 0;
for (int i = 0; i < k - 1; ++i) {
if (!b || !s[i]) break;
if (b == 1) {
b = 0, ++ret;

4
break;
}
if (s[i] == 1) --b, ++ret;
else b -= 2, ret += s[i];
}
return ret;
}

int main() {
cin >> t;
while (t--) {
scanf("%d %d %d", &n, &m, &k);
for (int i = 0; i < k; ++i) {
scanf("%d", a + i);
}
sort(a, a + k);
int st = a[0] - 1, en = n - a[k - 1];
for (int i = 1; i < k; ++i) {
s[i - 1] = a[i] - a[i - 1] - 1;
}
sort(s, s + k - 1);
reverse(s, s + k - 1);
int ans = 0;
ans = max(st + calc(m - 1), ans);
ans = max(st + en + calc(m - 2), ans);
ans = max(en + calc(m - 1), ans);
ans = max(calc(m), ans);
printf("%d\n", n - ans);
}
return 0;
}

5 War in Treeland
Setter. Rayhan Dhruboo, BUET Tester. Raihat Zaman Neloy, Google

Abridged Statement. Given a weighted tree, find the number of paths in it such that
the greatest common divisor of the edge weights on the path is divisible by a given positive
integer x.

Solution. The key is to notice the that the given condition is equivalent to the condi-
tion that all the edge weights on the path is divisible by x. The rest is simple: remove all
the edges whose weights are not divisible by x. We’ll get a forest (a bunch of trees). Any
m
path in one of these trees is a valid path. A tree of size m has exactly paths (each
2

5
pair of vertices corresponds to the unique path  between
 them).
  So if the forest consists of k
n1 nk
trees of sizes n1 , . . . , nk then the answer is + ··· + . We can compute the sizes
2 2
using depth first search.

Sample Code. O (n)


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 100010;

ll size;
bitset <N> vis;
vector <int> g[N];
int n, x, p, q, r;

void dfs (int u, int par = -1) {


vis[u] = 1, ++size;
for (int v : g[u]) if (v - par) dfs(v, u);
}

int main() {
cin >> n >> x;
for (int i = 1; i < n; ++i) {
scanf("%d %d %d", &p, &q, &r);
if (r % x) continue;
g[p].push_back(q);
g[q].push_back(p);
}
ll ans = 0;
for (int i = 1; i <= n; ++i) {
if (vis[i]) continue;
size = 0; dfs(i);
ans += size * (size - 1) / 2;
}
cout << ans << endl;
return 0;
}

6
6 French Fries
Setter. Sourav Sen Tonmoy, DU Tester. Tanzir Islam, DU

Abridged Statement. Given the prices of n food items, partition them into some non-
empty groups such that the number of groups with sum at least L = 50 is maximized.

Solution. Let’s denote by f (S) the answer for a set of food items S. Obviously f (∅) = 0.
Otherwise we either make the whole set S a single group, or we divide S into two disjoint
subsets T and S \ T and partition them separately. This gives us the recursive definition

f (S) = max{f (T ) + f (S \ T ) | T ⊆ S}

so we can solve this problem via dynamic programming using bitmasks in O (3n ). Due to the
model being a subset convolution in the max-sum semiring, the problem can also be solved
in O (n2 2n ).

Sample Code. O (T 3n )
#include <bits/stdc++.h>

using namespace std;

const int N = 17, L = 50;


const int M = (1 << 14) + 5;

int t, n, a[N], f[M];

int main() {
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%d", a + i);
for (int i = 1; i < 1 << n; ++i) {
int sum = 0;
for (int j = 0; j < n; ++j) if (i & 1 << j) sum += a[j];
f[i] = sum >= L;
for (int j = i; j > 0; j = (j - 1) & i) {
f[i] = max(f[i], f[j] + f[i ^ j]);
}
}
printf("%d\n", f[(1 << n) - 1]);
}
return 0;
}

You might also like