Brownstone
Algorithms / Programming / Cloud / Problem Solving / Maths
Sunday 17 April 2022
A Fun Problem - Math
# Problem Statement
JATC's math teacher always gives the class some interesting math problems so that they don't get bored. Today the problem is as follows. Given an integer $n$, you can perform the following operations zero or more times:
mul $x$: multiplies $n$ by $x$ (where $x$ is an arbitrary positive integer).
sqrt: replaces $n$ with $sqrt(n)$ (to apply this operation, $sqrt(n)$ must be an integer).
You can perform these operations as many times as you like. What is the minimum value of 𝑛, that can be achieved and what is the minimum number of operations, to achieve that minimum value?
Apparently, no one in the class knows the answer to this problem, maybe you can help them?
Input
The only line of the input contains a single integer $n$ $(1 <= 𝑛 <= 10^6)$ — the initial number.
Output
Print two integers: the minimum integer 𝑛 that can be achieved using the described operations and the minimum number of operations required.
# Solution
We can prime-factorize $n$ and get $p_1^{a_1} * p_2^{a_2} * ... * p_k^{a_k}$. At the end, we won't be able to remove the prime factors so the answer must be in the form of $p_1 * p_2 * ... * p_k$. If $a_i$ is a power of 2, then we can apply sqrt operation and make $a_n := \frac{a_n}{2}$. Otherwise, we can use the first operation to make all $a_i$ equal to $2 ^ X$. Therefore, the number of operations is either $X$ or $X + 1$.
```cpp
vector<long long> factorize(int x) {
vector<long long> res;
for (int y = 2; y * y <= x; y++) {
if (x % y) continue;
while(x % y == 0) {
res.push_back(y);
x /= y;
}
}
if (x > 1) res.push_back(x);
return res;
}
void solve() {
int n; cin >> n;
vector<long long> f = factorize(n);
map<long long , long long > m;
for (auto x : f) m[x]++;
long long mx = 0;
for (auto x : m) mx = max(mx, x.second);
long long b = 0;
while ((1 << b) < mx) b += 1;
long long ops = b, all_same = 1;
for (auto x : m) {
if (x.second != (1 << b)) {
all_same = 0;
break;
}
}
ops += all_same == 0;
long long p = 1;
for (auto x : m) p *= x.first;
cout << p << " " << ops << endl;
}
```
Wednesday 2 February 2022
Construct Quad Tree
A quadtree is a tree data structure in which each internal node has exactly four children.
Given a n * n matrix grid of 0's and 1's only. We want to represent the grid with a Quad-Tree.
Return the root of the Quad-Tree representing the grid.
Notice that you can assign the value of a node to True or False when isLeaf is False, and both are accepted in the answer.
Definition for a QuadTree node.
```cpp
class Node {
public:
bool val;
bool isLeaf;
Node* topLeft;
Node* topRight;
Node* bottomLeft;
Node* bottomRight;
Node() {
val = false;
isLeaf = false;
topLeft = NULL;
topRight = NULL;
bottomLeft = NULL;
bottomRight = NULL;
}
Node(bool _val, bool _isLeaf) {
val = _val;
isLeaf = _isLeaf;
topLeft = NULL;
topRight = NULL;
bottomLeft = NULL;
bottomRight = NULL;
}
Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) {
val = _val;
isLeaf = _isLeaf;
topLeft = _topLeft;
topRight = _topRight;
bottomLeft = _bottomLeft;
bottomRight = _bottomRight;
}
};
```
We can construct a Quad-Tree from a two-dimensional area using the following steps:
If the current grid has the same value (i.e all 1's or all 0's) set isLeaf True and set val to the value of the grid and set the four children to Null and stop.
If the current grid has different values, set isLeaf to False and set val to any value and divide the current grid into four sub-grids as shown in the photo.
Recurse for each of the children with the proper sub-grid.
If you want to know more about the Quad-Tree, you can refer to the wiki.
Quad-Tree format:
The output represents the serialized format of a Quad-Tree using level order traversal, where null signifies a path terminator where no node exists below.
It is very similar to the serialization of the binary tree. The only difference is that the node is represented as a list [isLeaf, val].
If the value of isLeaf or val is True we represent it as 1 in the list [isLeaf, val] and if the value of isLeaf or val is False we represent it as 0.
Example:
```cpp
Input: grid = [[0,1],[1,0]]
Output: [[0,1],[1,0],[1,1],[1,1],[1,0]]
```
Solution:
Recursively divide the grid by four and check if it is the leaf and its value is same as the other three.
```cpp
Node* construct(vector>& grid) {
int n = (int) grid.size();
return build(grid, 0, 0, n);
}
Node* build(vector>& grid, int i, int j, int n) {
if(n == 1) return new Node(grid[i][j], true);
Node* res = new Node(0, false,
build(grid, i, j, n / 2),
build(grid, i, j + n / 2, n / 2),
build(grid, i + n / 2, j, n / 2),
build(grid, i + n / 2, j + n / 2, n / 2));
if(
res->topLeft->isLeaf &&
res->topRight->isLeaf &&
res->bottomLeft->isLeaf &&
res->bottomRight->isLeaf &&
res->topLeft->val == res->topRight->val &&
res->topLeft->val == res->bottomLeft->val &&
res->topLeft->val == res->bottomRight->val
) {
res->val = res->topLeft->val;
res->isLeaf = true;
delete res->topLeft;
delete res->topRight;
delete res->bottomLeft;
delete res->bottomRight;
res->topLeft = NULL;
res->topRight = NULL;
res->bottomLeft = NULL;
res->bottomRight = NULL;
}
return res;
}
```
Wednesday 19 January 2022
1622C - Set or Decrease
Give an integer array $a_1, a_2, ..., a_n$ and an integer $k$. If you can either make $a_i = a_i - 1$ or $a_i = a_j$, what is the minimum number of steps you need to make the sum of array $\sum_{i=1}^n a_i <= k$?
Let's do it in a greedy way. Sort the array in an non-deceasing order. Apply operation one on $a_1$ $x$ times and apply operation two from $a_n$ $y$ times. The final array would look like $(a_1 - x), a_2, a_3, ..., a_{n - y}, (a_1 - x), (a_1 - x), ..., (a_1 - x)$. So we need to minimize the value of $x + y$ and we need $(a_1 - x) * (y + 1) + pref(n - y) - a_1 <= k$ where $pref$ is the prefix sum which can be precomputed beforehand. Rearrange it to get the minimum possible $x$ by iterating $y$ ,
$$
x = a_1 - \lfloor \frac{k - pref(n - y) + a_1}{y + 1} \rfloor
$$
Solution:
```cpp
long long safe_floor(long long x, long long y) {
long long res = x / y;
while (res * y > x) res--;
return res;
}
void solve() {
long long n, k; cin >> n >> k;
vector<long long> a(n);
long long sum = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a.begin(), a.end());
vector<long long> pref(n + 1);
for (int i = 0; i < n; i++) {
pref[i + 1] = pref[i] + a[i];
}
long long ans = 9e18;
for (int y = 0; y < n; y++) {
long long x = a[0] - safe_floor(k - pref[n - y] + a[0], y + 1);
x = max(0LL, x);
ans = min(ans, x + y);
}
cout << ans << endl;
}
```
Thursday 13 January 2022
CF1625C - Road Optimization
Problem : [Codeforces Round #765 (Div. 2) - C. Road Optimization](https://codeforces.com/contest/1625/problem/C)
![image](https://user-images.githubusercontent.com/35857179/149333000-60999ae8-4b6f-4fef-9dbc-18a66ee07fc9.png)
let $dp[i][j]$ be the minimum time to move up to sign $i$ and $j$ signs where $j$ is $[1 .. i - 1]$ are removed.
We can iterate all the stops, try all $k$ on each possible position $p$ where $p$ is the previous stops $1 .. p - 1$. For example, if we need to move from stop $p$ to sign $i$ directly, then all signs between these two stops has to be removed. The total time is simply $(d[i] - d[p]) * a[p]$. And we need to add the previous part $dp[p][j - (i - p - 1)]$. If we remove all signs from stop $p$ to $i$, so basically it is $i - p - 1$. There is a constraint that we cannot remove no more than $k$ signs, so we check if previous $j$ signs that have been removed is greater than $0$ or not. If so, then we can update take the minimum of $dp[i][j]$ and $dp[p][j - (i - p - 1)]$.
```cpp
void solve() {
long long n, l, k;
cin >> n >> l >> k;
vector<long long> d, a;
for (int i = 0; i < n; i++) cin >> d[i];
for (int i = 0; i < n; i++) cin >> a[i];
d.push_back(l);
vector<vector<long long>> dp(n + 1, vector<long long>(k + 1, 1e18));
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= k; j++) {
for (int p = i - 1; p >= 0; p--) {
int old_j = j - (i - p - 1);
if (old_j >= 0) {
dp[i][j] = min(dp[i][j], dp[p][old_j] + (d[i] - d[p]) * a[p]);
}
}
}
}
cout << *min_element(dp.back().begin(), dp.back().end()) << endl;
}
```
Tuesday 11 January 2022
A problem at which I stared for an hour
Problem: [LC2127 - Maximum Employees to Be Invited to a Meeting](https://leetcode.com/problems/maximum-employees-to-be-invited-to-a-meeting/)
A company is organizing a meeting and has a list of n employees, waiting to be invited. They have arranged for a large circular table, capable of seating any number of employees.
The employees are numbered from 0 to n - 1. Each employee has a favorite person and they will attend the meeting only if they can sit next to their favorite person at the table. The favorite person of an employee is not themself.
Given a 0-indexed integer array favorite, where favorite[i] denotes the favorite person of the ith employee, return the maximum number of employees that can be invited to the meeting.
![image](https://user-images.githubusercontent.com/35857179/149336435-73bea07a-db07-4090-b4d8-15664913417a.png)
Example:
##Input:
favorite = [2,2,1,2]
##Output:
3
## Explanation:
The above figure shows how the company can invite employees 0, 1, and 2, and seat them at the round table.
All employees cannot be invited because employee 2 cannot sit beside employees 0, 1, and 3, simultaneously.
Note that the company can also invite employees 1, 2, and 3, and give them their desired seats.
The maximum number of employees that can be invited to the meeting is 3.
## Solution
If an employee A has a favourite person, let's say employee B, and vice versa. Then we can put them together. Then we can put an employee, let's say C, whose favourite person is A on the left hand side of A. Then put an employee, let's say D, whose favourite person is C next to C. If we do the same thing for employee B, then we can have two ways to extend. Therefore, we can first look for the interdependent nodes, in this case, A & B.
```
if (a[a[i]] == i) {
// TODO: calculate the left chain and the right chain
}
```
Starting from node A and node B, we perform dfs to calculate the maximum nodes of the left chain and the right chain. Then we could invite $left + right + 2$ people.
```
function<int(int)> dfs = [&](int u) {
if (depth[u] != -1) return depth[u];
int res = 0;
for (int x : inv[u]) res = max(res, dfs(x));
return depth[u] = res + 1;
};
```
However, it would fail for the input [1,2,0] because it will output $0$ instead of $3$. In this case, we need to take care of the cyclic dependency. We need to run another dfs function for each node and check if there is a cyclic dependency. If the visited node is the entry node, then we know there is a cycle here. Then we could invite them also.
```
function<tuple<int, int, int>(int)> dfs2 = [&](int u)->tuple<int, int, int> {
if (depth[u] != -1) {
return { u, depth[u], 0 };
}
depth[u] = 0;
auto [entry, d, isCyclic] = dfs2(a[u]);
if (isCyclic) {
return { entry, d, 1 };
}
depth[u] = d + 1;
return {
entry,
depth[u],
u == entry
};
};
```
The final answer is simple the maximum number of the result of case 1 and case 2. Here's the full solution.
```
class Solution {
public:
int maximumInvitations(vector<int>& a) {
int n = a.size();
vector<int> depth(n, -1);
vector<vector<int>> inv(n);
for (int i = 0 ; i < n; i++) inv[a[i]].push_back(i);
// check interdependent nodes + longest left & right chain
function<int(int)> dfs = [&](int u) {
if (depth[u] != -1) return depth[u];
int res = 0;
for (int x : inv[u]) res = max(res, dfs(x));
return depth[u] = res + 1;
};
int mx1 = 0, mx2 = 0;
for (int i = 0; i < n; i++) {
if (depth[i] != -1) continue;
if (a[a[i]] == i) {
depth[i] = depth[a[i]] = 0;
int left = 0, right = 0;
for (int x : inv[i]) if (x != a[i]) left = max(left, dfs(x));
for (int x : inv[a[i]]) if (x != a[i]) right = max(right, dfs(x));
mx1 += left + right + 2;
}
}
// check cyclic dependency
function<tuple<int, int, int>(int)> dfs2 = [&](int u)->tuple<int, int, int> {
if (depth[u] != -1) {
return { u, depth[u], 0 };
}
depth[u] = 0;
auto [entry, d, isCyclic] = dfs2(a[u]);
if (isCyclic) {
return { entry, d, 1 };
}
depth[u] = d + 1;
return {
entry,
depth[u],
u == entry
};
};
for (int i = 0; i < n; i++) {
if (depth[i] != -1) continue;
auto [entry, d, isCyclic] = dfs2(i);
if (isCyclic) {
mx2 = max(mx2, d);
}
}
return max(mx1, mx2);
}
};
```
Subscribe to:
Posts (Atom)
A Fun Problem - Math
# Problem Statement JATC's math teacher always gives the class some interesting math problems so that they don't get bored. Today t...
-
SHA stands for Secure Hashing Algorithm and 2 is just a version number. SHA-2 revises the construction and the big-length of the signature f...
-
Contest Link: [https://www.e-olymp.com/en/contests/19775](https://www.e-olymp.com/en/contests/19775) Full Solution: [https://github.com/...