diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..a688a216 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ + + +Closes **TODO**. + + + +Problem link: **TODO**. + +### Changes introduced in this PR + + + +**TODO** + diff --git a/.github/workflows/inactive_issue.yml b/.github/workflows/inactive_issue.yml new file mode 100644 index 00000000..275b0de2 --- /dev/null +++ b/.github/workflows/inactive_issue.yml @@ -0,0 +1,28 @@ +name: Close inactive issues + +on: + # Run this workflow weekly on Friday at 00:00 UTC + # [cron](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07) + schedule: + - cron: "0 0 * * 5" + +jobs: + close-inactive-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + # https://github.com/marketplace/actions/close-stale-issues + - uses: actions/stale@v5 + with: + days-before-issue-stale: 30 + days-before-issue-close: 10 + stale-issue-label: "inactive" + stale-issue-message: "This issue was labeled `inactive` because it has been open for 30 days with no activity." + close-issue-message: "This issue was closed because there has been no activity for 10 days since being labeled as `inactive`." + # do not mark PRs as stale/inactive + days-before-pr-stale: -1 + # do not close PRs + days-before-pr-close: -1 + diff --git a/1012/bn.md b/1012/bn.md new file mode 100644 index 00000000..a4758745 --- /dev/null +++ b/1012/bn.md @@ -0,0 +1,103 @@ +# ক্যাটাগরি : ডি এফ এস + +### প্রশ্নে কি জিজ্ঞেস করেছে ? + +কাহিনীর অর্থে : একজন রাজা তার ছেলেকে রাজ্য ছাড়তে বলেন । পুত্র উনার কথা মান্য করে রাজ্য ছাড়ে । পরবর্তীতে রাজকুমার এমন একটি জায়গায় যান যেখানে প্রতিটি অঞ্চল হয় পানিতে পূর্ণ +অথবা জমিন । রাজকুমার জানতে চান উনার জায়গা হতে কতগুলা জমিন এ উনি যেতে পারবেন + +গাণিতিক অর্থে : একটি nxm গ্রিড দেয়া থাকবে । একটি পজিশন '@' দেয়া থাকবে যে জায়গা থেকে শুরু করে কয়টি সেল এ যেতে পারবো ? একটা সেল থেকে অন্য সেল এ যাওয়া যাবে যদি সেল টি তে '.' থাকে | আমরা কোন সেল এ '#' থাকলে যেতে পারবো না + +### যা যা শিখা উচিত এইটা করার আগে : +১ [ডি এফ এস ](http://www.shafaetsplanet.com/?p=973 ) (এটি বি এফ এস দিয়ে ও করা যাবে ) + +## সমাধান + +আমরা প্রথমে একটি nxm ক্যারেক্টার অ্যারে তে আমাদের পুরো গ্রিড স্টোর করবো +নিচের উদাহরণ এর মত একটি 2D অ্যারে তে আমরা ক্যারেক্টার গুলা নিব + +![alt text](https://user-images.githubusercontent.com/72943111/202870913-02f03555-4222-4eed-a889-b6aee8117c38.png) + + +প্রতিটি সেল সারি x এবং কলাম y নির্দেশ করে । সমস্যাটি হিসাবে একটি ঘর থেকে তার সংলগ্ন বাম, ডান, উপরে এবং নীচের ঘরে যেতে পারি। তাই একটি ঘর থেকে তার সংলগ্ন ঘরে যেতে আমাদের নিম্ন বর্ণিত কাজ করতে হবে: + +``` +Left - (x-1,y) +Right - (x+1,y) +Up - (x,y-1) +Down - (x,y+1) +``` + +প্রতিবার ম্যানুয়ালি এটি করার পরিবর্তে আমরা ডিরেকশনাল অ্যারে ব্যবহার করতে পারি এবং লুপ এর সাহায্যে তা ট্রাভেরস করতে পারি + +``` +int dx[]={+1,-1,+0,-0} +int dy[]={+0,-0,+1,-1} +``` + +এখন প্রারম্ভিক বিন্দু @ থেকে, আমরা প্রতিটি সংলগ্ন কোষে যাই এবং এটি একটি বৈধ সেল কিনা তা পরীক্ষা করি। আমাদের মনে রাখতে হবে যে আমরা গ্রিড এর বাইরে , ওয়াটার সেল # এবং যে কোষগুলি আগে পরিদর্শন করেছি ঐগুলা তে যেতে পারবো না । এটি আমরা চেক করার জন্য একটি ফাংশন লিখতে পারি। +``` +bool valid(int x,int y) +{ + if(x>=0 && x=0 && y +using namespace std; +char A[21][21]; +int cnt=0,vis[21][21],n,m; +void dfs(int x,int y) +{ + if(x==n || y==m ||x==-1 || y==-1) + return; + if(vis[x][y]==1) + return ; + if(A[x][y]=='#') + return ; + cnt++; + vis[x][y]=1; + dfs(x+1,y); + dfs(x,y+1); + dfs(x-1,y); + dfs(x,y-1); +} +int main() +{ + int t,tes=1; + cin>>t; + while(t--) + { + int x,y; + cin>>m>>n; + string s; + for(int i=0; i>s; + for(int j=0; j +using namespace std; +#define pi 2*acos(0.0) +int main() +{ + int tes=1,t; + cin>>t; + while(t--) + { + double a,b; + cin>>a; + b=a*2; + printf("Case %d: %0.2lf\n",tes++,b*b-pi*a*a); + } +return 0; +} +``` + + + diff --git a/1030/en.md b/1030/en.md index d310f350..cec61b27 100644 --- a/1030/en.md +++ b/1030/en.md @@ -54,3 +54,4 @@ int main() { cs++; } } +``` diff --git a/1036/en.md b/1036/en.md new file mode 100644 index 00000000..83c0ff89 --- /dev/null +++ b/1036/en.md @@ -0,0 +1,247 @@ +# LOJ 1036 - A Refining Company + +--- +Tags: Dynamic Programming, Best Cumulative Sum, Memoization + +The problem statement has these key points: + +1. Conveyor belts moving up-down in the same column from block __b__ to the __radium refinery__ can be added up as long as there is no left-right conveyor belt moving in between them. +2. Conveyor belts moving left-right in the same row from block __b__ to the __uranium__ can be added up as long as there is no up-down conveyor belt moving in between them. +3. Any kind of conveyor is NOT _bi-directional_ when it comes to collection. It has to reach the refinery, in one direction. Either from right->left or from down->up, BUT left->right or up->down is not possible as the refineries’ placement is already given. This plays a heavy role in how to implement and memoize left-right (left->right or right->left?) and up-down (up->down or down->up?). + +We need to find the maximum possible cumulative sum that can be reached by following the above-mentioned rules. + +### Helpful Resources + +* [Memoization - WikiPedia](https://en.wikipedia.org/wiki/Memoization "Memoization - Wikipedia") + +* [Memoization (1D, 2D and 3D) - GeeksForGeeks](https://www.geeksforgeeks.org/memoization-1d-2d-and-3d/ "Memoization (1D, 2D and 3D)") + +* [What is Dynamic Programming? - educative.io](https://www.educative.io/courses/grokking-dynamic-programming-patterns-for-coding-interviews/m2G1pAq0OO0 "[What is Dynamic Programming?") + +## Solution (Key Points) + +There are 2 main sub-problems that we need to solve to get the final answer. They are: + +1. What is the cumulation of each block resource variation-wise separately? We need this for comparison. +2. A block should be part of which type of conveyor? The actual decision-making part. + +## 1. What is the cumulation of each block resource variation-wise separately? + +For this just add up the immediately previous block’s value with the current block’s value according to the conveyor type while taking inputs. Here the direction is important. We need to cumulative in the opposite direction of the refineries. Since the refineries are in left and up, cumulative in right and down direction. For `uraniumMatrix` it's `[row][column-1]` and for `radiumMatrix` it's `[row-1][column]`. + +### Why does cumulation direction matter? + +Compare the following 2 for a simple left-right cumulation. + +Towards the refinery: + +||0|1|2|3| +|---|---|---|---|---| +|0|<|<|^|x| + +If we cumulate towards the refinery, the direction of cumulation will be `(0,3) -> (0,2) -> (0,1) -> (0,0)`. But `(0,2)` cuts the path. So both `(0,0)` and `(0,1)` is _faulty_ cumulated. + +Opposite of the refinery: + +||0|1|2|3| +|---|---|---|---|---| +|0|>|>|^|x| + +In this case, `(0,0) -> (0,1) -> (0,2) -> (0,3)` is the direction. So `(0,0)`, `(0,1)` is still correctly cumulated even if `(0,2)` is going upwards. + +## 2. A block should be part of which type of conveyor? + +While addressing subproblem-1, we did not take into account the alternative resource for any of the matrices. We just do a tweaked cumulation sum comparing these 2 matrices and take the best among them in a third matrix, `memoizationMatrix`. + +### What are the cases for ultimate cumulation? + +* Uranium is more: `memoizationMatrix[i-1][j] + uraniumMatrix[i][j]`. +* Radium is more: `memoizationMatrix[i][j-1] + radiumMatrix[i][j]`. + +### How are we doing the ultimate cumulation (reason behind choosing between `memoizationMatrix[i-1][j] + uraniumMatrix[i][j]` or `memoizationMatrix[i][j-1] + radiumMatrix[i][j]`)? + +||0|1|2|3| +|---|---|---|---|---| +|0|x|x|x|x| +|1|x|x|x|x| +|2|x|x|x|x| +|3|x|x|x|x| +|4|x|x|x|x| + +Taking a look at the above, we need to know what is the ultimate cumulative sum of from `(0,0)` to `(2,3)` if we consider a block has been carrying the summation of its previous blocks either left-right (uranium/left-right) or up-down (radium/up-down)? + +left-right: + +||0|1|2|3| +|---|---|---|---|---| +|0|>|>|>|>| +|1|>|>|>|>| +|2|>|>|>|>| + +{`>` is summed up with its value and the __IMMEDIATE__ previous block's value} + +Here, `((0,3) + (1,3) + (2,3))` [this is up->down, look at the image] = ULTIMATE CUMULATIVE SUM. Because while __solving sub-problem-1__, we made sure `(0,3) = (0,0) + (0,1) + (0,2) + (0,3)`, `(1,3) = (1,0) + (1,1) + (1,2) + (1,3)` and `(2,3) = (2,0) + (2,1) + (2,2) + (2,3)`, which was adding left->right. But we need `sum(each cumulated block)`, which is __actually__ from up->down of the cumulated left-right blocks. Basically, __(M0,j + M1,j + ... + Mn,j)__ is applicable for left-right ULTIMATE CUMULATIVE SUM for up to __Mn,j__ block. + +up-down: + +||0|1|2|3| +|---|---|---|---|---| +|0|v|v|v|v| +|1|v|v|v|v| +|2|v|v|v|v| + +{`v` is summed up with its value and the __IMMEDIATE__ previous block's value} + +Similarly, here, `((2,0) + (2,1) + (2,2) + (2,3))` [this is left->right, look at the image] = ULTIMATE CUMULATIVE SUM. Because while __solving sub-problem-1__, we made sure `(2,0) = (0,0) + (1,0) + (2,0)`, `(2,1) = (0,1) + (1,1) + (2+1)`, `(2,2) = (0,2) + (1,2) + (2,2)` and `(2,3) = (0,3) + (1,3) + (2,3)`, which was adding up->down. But we need `sum(each cumulated block)`, which is __actually__ from left->right of the cumulated up->down blocks. Basically, __(Mi,0 + Mi,1 + ... + Mi,n)__ is applicable for up-down ULTIMATE CUMULATIVE SUM for up to __Mi,n__ block. + +We have established how are we doing full matrix cumulation up to a certain block left-right or up-down wise. + +Now for a matrix, + +||0|1|2|3| +|---|---|---|---|---| +|0|x|x|x|x| +|1|x|x|x|x| +|2|x|x|x|x| +|3|x|x|x|x| +|4|x|x|x|x| + +If `x` holds the updated value then we can easily apply any of the above-mentioned block-wise cumulations, and then compare, right? The 3rd matrix is required for that. That's why `memoizationMatrix[i-1][j]` is the replacement of `uraniumMatrix[i-1][j]` and `memoizationMatrix[i][j-1]` is the replacement of `radiumMatrix[i][j-1]` to apply the left-right and up-down ULTIMATE CUMULATION SUM respectively. And if we have the updated value, it becomes irrelevant how `x` was obtained because the ultimate goal is to carry out the maximum cumulative sum block by block until the last block is met. THE CROSS PATHS WERE ALREADY HANDLED IN SUB-PROBLEM 1. + +### Why a 3rd Matrix and not just update the values of the original? + +We can't over-write the original 2 matrices because while iterating, we can't be sure if there exists a single block of a particular conveyor that can outweigh the whole alternative conveyor up to that block. + +The above implementation is `accepted`. + +## Time Complexity + +2D Array -> mutation : O(1) + +2D Array -> look up : O(1) + +Whole 2D Array -> iteration : O(row x column) + +Overall Complexity : O(3 x row x column) = O(row x column) + +## Solution in Java (Iterative) + +* `Scanner in = new Scanner(new BufferedReader(new InputStreamReader(System.in)));` will throw `TLE` or `MLE`. + +```java +import java.io.DataInputStream; +import java.io.IOException; + +/* +The `Reader` class aids only in taking inputs. Use any alternative that satisfies the time and memory constraints. +https://www.geeksforgeeks.org/fast-io-in-java-in-competitive-programming/ - 4th implementation for fast Java I/O. +*/ + +class Reader { + final private int BUFFER_SIZE = 1 << 16; + private DataInputStream din; + private byte[] buffer; + private int bufferPointer, bytesRead; + + public Reader() { + din = new DataInputStream(System.in); + buffer = new byte[BUFFER_SIZE]; + bufferPointer = bytesRead = 0; + } + + public int nextInt() throws IOException { + int ret = 0; + byte c = read(); + while (c <= ' ') { + c = read(); + } + boolean neg = (c == '-'); + if (neg) + c = read(); + do { + ret = ret * 10 + c - '0'; + } while ((c = read()) >= '0' && c <= '9'); + + if (neg) + return -ret; + return ret; + } + + private void fillBuffer() throws IOException { + bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE); + if (bytesRead == -1) + buffer[0] = -1; + } + + private byte read() throws IOException { + if (bufferPointer == bytesRead) + fillBuffer(); + return buffer[bufferPointer++]; + } + + public void close() throws IOException { + if (din == null) + return; + din.close(); + } +} + + + +public class Main{ + + private static int FindMax(int a, int b){ + return a > b ? a : b; + } + + public static void main(String[] args) throws IOException { + Reader in = new Reader(); + int[][] radiumMatrix; // East to West. + int[][] uraniumMatrix; // South to North. + int[][] memoizationMatrix; // Result matrix and updated value holder. + + int testCases = in.nextInt(); + + for(int testCase = 1; testCase <= testCases; testCase++){ + int m = in.nextInt(); + int n = in.nextInt(); + + radiumMatrix = new int[m+1][n+1]; + uraniumMatrix = new int[m+1][n+1]; + memoizationMatrix = new int[m+1][n+1]; + + // Input for Uranium + for(int i = 1; i <= m; i++){ + for(int j = 1; j <= n; j++){ + uraniumMatrix[i][j] = in.nextInt(); + + // East to West cumulative sum memoization + uraniumMatrix[i][j] += uraniumMatrix[i][j-1]; + } + } + + // Input for Radium + for(int i = 1; i <= m; i++){ + for(int j = 1; j <= n; j++){ + radiumMatrix[i][j] = in.nextInt(); + + // South to North cumulative sum memoization + radiumMatrix[i][j] += radiumMatrix[i-1][j]; + } + } + + /*Memoization Matrix + - Treat this as the actual matrix for cumulation from the previous block + */ + for(int i = 1; i <= m; i++) + for(int j = 1; j <= n; j++) + memoizationMatrix[i][j] = FindMax( + memoizationMatrix[i-1][j] + uraniumMatrix[i][j], + memoizationMatrix[i][j-1] + radiumMatrix[i][j]); + + System.out.println(String.format("Case %s: %s",testCase ,memoizationMatrix[m][n])); + } + } +} +``` diff --git a/1037/en.md b/1037/en.md new file mode 100644 index 00000000..2221ee27 --- /dev/null +++ b/1037/en.md @@ -0,0 +1,90 @@ +# LOJ 1037 - Agent 47 + +## Summary +Given **n** targets and a **n x n** matrix per test case: + +v[**i**] = number of shots to kill **i**'th target using **KM .45 Tactical (USP)** . 0 <= i < n +cost[**i**][**j**] = number of shots to kill **j**'th target using weapon provided from **i**'th arsenal after killing **i**'th target itself. + +We have to compute the minimum number of shots to kill all targets using **KM .45 Tactical (USP)** and weapons from the arsenals of already killed targets. + +## Prerequisite + +Bitmask Dynamic Programming + +Great source for learning bitmask DP for the first time: https://www.hackerearth.com/practice/algorithms/dynamic-programming/bit-masking/tutorial/ + +## Solution +Suppose we have to kill all targets of the set **S** = {2, 3, 5, 7}. We will choose a target to kill at last (for, say 5) then excluding 5 from **S** gives a new set **G** = {2, 3, 7}. Now, if we know the optimal answer for the smaller set **G**, we can efficiently compute the answer for **S** considering target 5 being the last to kill. Using the weapons from the arsenals of targets belonging to **G** that are provided to kill target 5, we take the weapon that kills it with minimum shots and add this to the result of **G** to get the required answer for **S**. Now, that might not be the optimal result for **S**. So we try all targets from **S** to see which target being the last to kill gives the best result. + +So the algorithm is pretty straightforward. For each mask we brute force over all distinct pairs (**i**, **j**) **contained in the mask** where considering **i's** being the last to kill, we try weapons from **j's** arsenals to kill **it** and take the minimum result overall. This will ensure the optimum result for that particular mask. Doing that for all masks in increasing order, and our result will be the mask that contains every target. + +## Complexity +- Time Complexity: `O(T * (2^n * n^2))`. +- Memory Complexity: `O(2^n)`. + +## Code + +### C++ + +```cpp +#include + +using namespace std; + + +int main() { + + // for fast I/O + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + const int INF = 1e9; + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + int n; + cin >> n; + + vector v(n, 0); + vector > cost(n+1, vector (n, INF)); + for(int i = 0; i < n; ++i) { + cin >> v[i]; + cost[i][i] = 0; + } + + for(int i = 0; i < n; ++i) { + string str; + cin >> str; + for(int j = 0; j < n; ++j) { + int w = str[j] - '0'; + cost[i][j] = min(cost[i][j], v[j]); // KM.45 Tactical (USP) is always available to use + if (w != 0) cost[i][j] = min(cost[i][j], v[j] / w + (v[j] % w != 0? 1:0)); // updating with j'th weapon from i'th arsenal + } + } + + // dp[mask] = minimum cost of killing all targets in the 'mask' + vector dp(1< + +using namespace std; + + +struct NewData +{ + string city1, city2; + int cost; +} vr; + +bool operator<(struct NewData d1, struct NewData d2) +{ + return d1.cost < d2.cost; +} + +vector graph; +map Checked; +map parent; +set st; +map> mp; + +void ClearAll() +{ + graph.clear(); + Checked.clear(); + parent.clear(); + st.clear(); + mp.clear(); +} + +string findParent(string s) +{ + if (parent[s] == s) + return s; + return findParent(parent[s]); +} + +int MinCost() +{ + int res = 0; + for (auto x : st) + { + parent[x] = x; + } + for (auto x : graph) + { + string s = x.city1; + string s2 = x.city2; + string ps = findParent(s); + string ps2 = findParent(s2); + if (ps != ps2) + { + res += x.cost; + parent[ps2] = ps; + } + } + return res; +} + +int bfs(string s) +{ + int res = 0; + queue q; + q.push(s); + Checked[s] = true; + res++; + while (!q.empty()) + { + string cur = q.front(); + q.pop(); + for (auto x : mp[cur]) + { + if (!Checked[x]) + Checked[x] = true, q.push(x), res++; + } + } + return res; +} + +int main() +{ + int t, cs = 1; + cin >> t; + while (t--) + { + int n; + cin >> n; + ClearAll(); + while (n--) + { + cin >> vr.city1 >> vr.city2 >> vr.cost; + graph.push_back(vr); + st.insert(vr.city1); + st.insert(vr.city2); + mp[vr.city1].insert(vr.city2); + mp[vr.city2].insert(vr.city1); + } + sort(graph.begin(), graph.end()); + string ans = ""; + int mnCost = MinCost(); + ; + if (bfs(*st.begin()) != st.size()) + ans = "Impossible\n"; + cout << "Case " << cs++ << ": "; + if (ans.size() > 0) + cout << ans; + else + cout << mnCost << endl; + } + return 0; +} +``` + +## Resources: +- [BFS] (https://cp-algorithms.com/graph/breadth-first-search.html) +- [DFS] (https://cp-algorithms.com/graph/depth-first-search.html) +- [Kruskal] (https://cp-algorithms.com/graph/mst_kruskal.html) +- [Prim's] (https://cp-algorithms.com/graph/mst_prim.html) + + +### Tutorial by: +Profile Link: [Hasibur Rahman] (https://lightoj.com/user/evan13) \ No newline at end of file diff --git a/1044/en.md b/1044/en.md new file mode 100644 index 00000000..7077d771 --- /dev/null +++ b/1044/en.md @@ -0,0 +1,82 @@ +# LOJ 1044 - Palindrome Partitioning + +## Summary +Given a string, we have to split it into its substrings so that all of them are palindromes, and the number of such substrings is minimized. + +## Prerequisite +Basic Dynamic Programming + +## Hint +Removing the first and the last character from a palindrome also results in a palindrome. For example: "a**bacab**a" is a palindrome and "b**aca**b" is also a palindrome so is "aca". + +## Solution +Let's define: + +isPalindrome[i][j] = whether the substring S[i...j] is a palindrome or not + +dp[j] = minimum number of palindromic substrings that split the prefix S[0...j] + +We compute the **isPalindrome** matrix beforehand using the above hint. However, in order to know whether the substring S[i..j] is a palindrome or not, we must know if the substring S[i+1.....j-1] is a palindrome or not. So we will go in increasing length of the substrings to determine whether the substring S[i...j] is a palindrome or not. + +For a string S[0...j], if we take a palindromic suffix S[i..j] (0 <= i <= j), then the resulting string (excluding the suffix) has been reduced to a smaller subproblem to solve, S[0...i-1] (could be an empty string). And the answer would be dp[i-1] + 1 (for the palindromic suffix we have chosen). We don't know which suffix will yield the best outcome, so we try all such suffixes to find the optimum answer. + +## Complexity +- Time Complexity: `O(T * (n^2))`. +- Memory Complexity: `O(n^2)`. + +## Code + +### C++ + +```cpp +#include + +using namespace std; + + +int main() { + + // for fast IO + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + const int INF = 1e9; + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + string str; + cin >> str; + + int n = str.length(); + + vector > isPalindrome(n, vector (n, false)); + for(int i = 0; i < n; ++i) { + for(int j = 0; j <= i; ++j) { + isPalindrome[i][j] = true; // "" (empty) strings and a single letter are said to be palindromes. + } + } + + for(int len = 2; len <= n; ++len) { + for(int i = 0; i + len - 1 < n; ++i) { + int j = i + len - 1; + isPalindrome[i][j] = isPalindrome[i+1][j-1] && (str[i] == str[j]); + } + } + + vector dp(n, n); + for(int j = 0; j < n; ++j) { + for(int i = j; i >= 0; --i) { + if (isPalindrome[i][j]) { + dp[j] = min(dp[j], (i > 0? dp[i-1]:0) + 1); + } + } + } + + cout << "Case " << ts << ": " << dp[n-1] << '\n'; + } + + return 0; +} +``` diff --git a/1070/en.md b/1070/en.md new file mode 100644 index 00000000..f346b899 --- /dev/null +++ b/1070/en.md @@ -0,0 +1,170 @@ +# LOJ 1070 - Algebraic Problem + +## Summary +Given three non-negative integers, a+b = **p**, ab = **q** and **n** we have to find the value of $a^n$ + $b^n$. a and b not necessarily have to be integers. There will be no such input so that we have to find the value of $0^0$ + +## Prerequisite +Matrix Exponentiation: https://www.youtube.com/watch?v=QcT5T-46iFA + +**unsigned long long int** in C/C++: https://www.geeksforgeeks.org/maximum-value-of-unsigned-long-long-int-in-c/ (language specific). Implementations of **unsigned long long int** have a modulo behavior when performing arithmetic operations. Any arithmetic manipulation with unsigned long long int's will be taken modulo $2^{64}$ automatically. + +## Solution +Let's define: + +F(n) = $a^n$ + $b^n$ + +=> F(0) = $a^0$ + $b^0$ = 2 + +=> F(1) = a + b = **p** + +$a^2$ + $b^2$ = $(a+b)^2$ - 2ab + +=> $a^2$ + $b^2$ = (a + b) * (a + b) - ab * ( $a^0$ + $b^0$ ) + +=> F(2) = $a^2$ + $b^2$ = **p** * (a + b) - **q** * ( $a^0$ + $b^0$ ) + +$a^3$ + $b^3$ = (a + b) * ( $a^2$ + $b^2$ ) - ab * (a + b) + +=> F(3) = $a^3$ + $b^3$ = **p** * ( $a^2$ + $b^2$ ) - **q** * (a + b) + +$a^4$ + $b^4$ = (a + b) * ( $a^3$ + $b^3$ ) - ab * ( $a^2$ + $b^2$ ) + +=> F(4) = $a^4$ + $b^4$ = **p** * ( $a^3$ + $b^3$ ) - **q** * ( $a^2$ + $b^2$ ) + +Observing the pattern we can conclude: + +F(n) = **p** * F(n-1) - **q** * F(n-2) + +It's a linear recurrence that can be solved using matrix exponentiation technique. + +$$ +\begin{pmatrix} +p & -q \\ +1 & 0 +\end{pmatrix} +\begin{pmatrix} +F(1) \\ +F(0) +\end{pmatrix} = +\begin{pmatrix} +F(2) \\ +F(1) +\end{pmatrix} +$$ + +$$ +\begin{pmatrix} +p & -q \\ +1 & 0 +\end{pmatrix} ^ {2} +\begin{pmatrix} +p \\ +2 +\end{pmatrix} = +\begin{pmatrix} +F(3) \\ +F(2) +\end{pmatrix} +$$ + +$$ +\begin{pmatrix} +p & -q \\ +1 & 0 +\end{pmatrix} ^ {n} +\begin{pmatrix} +p \\ +2 +\end{pmatrix} = +\begin{pmatrix} +F(n+1) \\ +F(n) +\end{pmatrix} +$$ + +## Complexity +- Time Complexity: O(T * $k^3$ $log{_2}{n}$). Here, k = 2 +- Memory Complexity: O( $k^2$ ). + +## Code + +### C++ + +```cpp +#include + +using namespace std; + + +typedef unsigned long long ull; + +struct Matrix { + vector > mat; + + Matrix(int n) { + mat.assign(n, vector (n, 0)); + } +}; + +Matrix mat_multiply(const Matrix& A, const Matrix& B) { + int n = A.mat.size(); + Matrix res(n); + + for(int i = 0; i < n; ++i) { + for(int j = 0; j < n; ++j) { + for(int k = 0; k < n; ++k) { + res.mat[i][j] += A.mat[i][k] * B.mat[k][j]; + } + } + } + + return res; +} + +Matrix binpow(Matrix &A, int power) { + int n = A.mat.size(); + Matrix res(n); + + for(int i = 0; i < n ; ++i) { + for(int j = 0; j < n; ++j) { + res.mat[i][j] = (i == j? 1:0); // identity matrix + } + } + + while (power > 0) { + if (power & 1) { + res = mat_multiply(res, A); + } + power >>= 1; + A = mat_multiply(A, A); + } + + return res; +} + +int main(int argc, const char *argv[]) { + + // for fast I/O + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + ull p, q, n; + cin >> p >> q >> n; + + Matrix A(2); + A.mat[0][0] = p; + A.mat[0][1] = -q; + A.mat[1][0] = 1; + + A = binpow(A, n); + + cout << "Case " << ts << ": " << A.mat[1][0]*p + (A.mat[1][1]<<1) << '\n'; + } + + return 0; +} +``` diff --git a/1077/en.md b/1077/en.md new file mode 100644 index 00000000..19d01157 --- /dev/null +++ b/1077/en.md @@ -0,0 +1,50 @@ +# LOJ 1077 - How Many Points? + + +## Solution +The number of lattice points lying on the segment **A(x1, y1) -> B(x2, y2)** is same for segment **A(0, 0) -> B(x2 - x1, y2 - y1)** because co-ordinate translation doesn't change the relative distance of lattice points from each other. +The number of lattice points will also be equal to that of segment **A(0, 0) -> B(|x2 - x1|, |y2 - y1|)** since sign (+/-) only tells us in which quadrants the segment will fall. +Number of lattice points is length dependent not quadrant dependent. + +So, how to calculate the number of lattice points on any segment **A(0, 0) -> B(x, y)** where **x, y >= 0**? The answer is: **gcd(x, y) + 1**. Why? + +Suppose, **g = gcd(x, y)** then, all the lattice points are: + +**(0 * x/g, 0 * y/g), (1 * x/g, 1 * y/g), . . . . . . . . . . . . . . . , ((g-1) * x/g, (g-1) * y/g), (g * x/g, g * y/g)** total of **(g + 1)** points with integer abscissas and ordinates. + +But what's the proof they lie on the segment **AB** and there can't be any other lattice points? + +See: https://math.stackexchange.com/questions/628117/how-to-count-lattice-points-on-a-line + +## Complexity +- Time Complexity: O(T * lg(N)). Where N = **max(a, b)** of **gcd(a, b)**. [Check](https://stackoverflow.com/questions/3980416/time-complexity-of-euclids-algorithm) for the time complexity of Euclid's GCD algorithm. +- Memory Complexity: O(1). + +## Code + +### C++ + +```cpp +#include + +using namespace std; + +int main() { + + // For fast I/O + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + pair A, B; + cin >> A.first >> A.second >> B.first >> B.second; + + cout << "Case " << ts << ": " << __gcd(abs(A.first - B.first), abs(A.second - B.second)) + 1 << '\n'; + } + + return 0; +} +``` diff --git a/1082/en.md b/1082/en.md new file mode 100644 index 00000000..e01de51a --- /dev/null +++ b/1082/en.md @@ -0,0 +1,235 @@ +# LOJ 1082 - Array Queries + +The problem statement is very clear. The only difficulty you will face here is the time limit. Naive approach will give TLE. Then what do we need to solve this problem? We could use Cumulative Sum technique if the problem wanted sum of ranges but Cumulative Sum doesn't work for min/max type problems. We need an efficient data structure. We can use Segment Tree. If you know about this data structure then you will notice that a basic Segment Tree implementation will be enough to solve this problem. + +### Helpful resources: + +* [Recursion tutorial - Smilitude](https://sites.google.com/site/smilitude/recursion_and_dp) +* [Segment Tree tutorial - Shafaet's blog](http://www.shafaetsplanet.com/?p=1557) +* [Segment Tree theory - Loveextendscode](https://www.youtube.com/watch?v=IQ1Xpn7EUMQ) +* [Segment Tree implementation - Loveextendscode](https://www.youtube.com/watch?v=olgP016uMjY) + +### Solution: + +First we will build the Segment Tree from the given array. The leaf nodes of the tree will store the array values. Any non-leaf node will store the minimum value of its left child and right child. Then we will just run the given queries on the tree and print the answers. The overall time complexity will be O(NlogN). + +Caution: for an array of size N, it is safe to keep the tree size 4*N. To know why, check this out: https://stackoverflow.com/a/28502243 + +### Code: +#### C++ +----- +```cpp +#include +using namespace std; + +vector arr; // For storing input array +vector seg_tree; // For storing the Segment Tree + +// Function for building the Segment tree. Here the parameters are: +// node -> Index of the current node in the segment tree. +// Begin -> Starting index of the segment of the array we are working on. +// End -> Ending index of the segment of the array we are working on. +void build(int node, int Begin, int End) +{ + // Leaf node because the size of the segment is 1. + if(Begin == End) + { + seg_tree[node] = arr[Begin]; + return; + } + + int Left, Right, Mid; + Left = node*2; // Left child of the current node. + Right = (node*2)+1; // Right child of the current node. + Mid = Begin+((End-Begin)/2); // Middle index of the current segment. + build(Left, Begin, Mid); // Building the left subtree for the first half of the current segment. + build(Right, Mid+1, End); // Building the right subtree for the second half of the current segment. + seg_tree[node] = min(seg_tree[Left], seg_tree[Right]); // Storing the minimum of the two halves in the current node. +} + +// Function for running the queries. Here the parameters are: +// node -> Index of the current node in the segment tree. +// Begin -> Starting index of the segment of the array. +// End -> Ending index of the segment of the array. +// I -> Starting index of the range we are running query on. +// J -> Ending index of the range we are running query on. +int query(int node, int Begin, int End, int I, int J) +{ + // Current segment and query range don't intersect. This segment has no contribution. + if(End>t; + + for(int cs=1; cs<=t; cs++) + { + int n, q; + cin>>n>>q; + arr.assign(n+2, 0); + seg_tree.assign((4*n)+2, 0); + + for(int i=1; i<=n; i++) + cin>>arr[i]; + + build(1, 1, n); // Building the Segment Tree + cout<<"Case "<>I>>J; + cout<= '0' && c <= '9'); + + if (neg) + return -ret; + return ret; + } + + private void fillBuffer() throws IOException { + bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE); + if (bytesRead == -1) + buffer[0] = -1; + } + + private byte read() throws IOException { + if (bufferPointer == bytesRead) + fillBuffer(); + return buffer[bufferPointer++]; + } + + public void close() throws IOException { + if (din == null) + return; + din.close(); + } +} + +public class Main { + + private static int segmentedTree[]; + + private static int GetMin(int x, int y) { + return (x < y) ? x : y; + } + + private static int ConstructSegmentedTree(int[] inputArray, int indexSegmentStart, int indexSegmentEnd, + int indexCurrent) { + if (indexSegmentStart == indexSegmentEnd) { + segmentedTree[indexCurrent] = inputArray[indexSegmentStart]; + return inputArray[indexSegmentStart]; + } + + int mid = indexSegmentStart + (indexSegmentEnd - indexSegmentStart) / 2; + segmentedTree[indexCurrent] = GetMin( + ConstructSegmentedTree(inputArray, indexSegmentStart, mid, indexCurrent * 2 + 1), + ConstructSegmentedTree(inputArray, mid + 1, indexSegmentEnd, indexCurrent * 2 + 2)); + return segmentedTree[indexCurrent]; + } + + private static int MakeQuery(int indexSegmentStart, int indexSegmentEnd, int indexQueryStart, int indexQueryEnd, + int index) { + if (indexQueryStart <= indexSegmentStart && indexQueryEnd >= indexSegmentEnd) + return segmentedTree[index]; + + if (indexSegmentEnd < indexQueryStart || indexSegmentStart > indexQueryEnd) + return Integer.MAX_VALUE; + + int mid = indexSegmentStart + (indexSegmentEnd - indexSegmentStart) / 2; + return GetMin(MakeQuery(indexSegmentStart, mid, indexQueryStart, indexQueryEnd, 2 * index + 1), + MakeQuery(mid + 1, indexSegmentEnd, indexQueryStart, indexQueryEnd, 2 * index + 2)); + } + + public static void main(String[] args) throws IOException { + Reader in = new Reader(); + int testCases = in.nextInt(); + + for (int testCase = 1; testCase <= testCases; testCase++) { + int[] inputArray = new int[in.nextInt()]; + int numberOfQueries = in.nextInt(); + + int length = inputArray.length; + for (int index = 0; index < length; index++) + inputArray[index] = in.nextInt(); + + int height = (int) (Math.ceil(Math.log(length) / Math.log(2))); + int size = 2 * (int) Math.pow(2, height) - 1; + segmentedTree = new int[size]; + ConstructSegmentedTree(inputArray, 0, length - 1, 0); + + System.out.println("Case "+testCase+":"); + for (int query = 1; query <= numberOfQueries; query++) { + int indexQueryStart = in.nextInt() - 1; + int indexQueryEnd = in.nextInt() - 1; + System.out.println ((MakeQuery(0, length - 1, indexQueryStart, indexQueryEnd, 0))); + } + } + + } +} +``` + + Happy coding! :3 diff --git a/1089/en.md b/1089/en.md new file mode 100644 index 00000000..f5212179 --- /dev/null +++ b/1089/en.md @@ -0,0 +1,113 @@ +# LOJ 1089 - Points in Segments (II) + +## Summary +The problem involves a set of one-dimensional segments and a set of points. The objective is to count, for each point, how many segments include that point within their range. + +Note: **AA** is a valid segment. + +## Solution +First let's simplify the problem a bit. Suppose there is no such segment like: **AA** + +Let's think of each segment **AB** as of two points where **A** is the opening of the segment and **B** the ending of it. + +Consider the set of all opening and ending points of the segments sorted in increasing order. Iterating over the set, there may be two scenarios: + +1. An opening point `A` of segment `AB` is found. `AB` has started. Any point that comes after `A` (or equal to `A`) until we reach the endpoint `B` will be on that segment. + +2. An ending point `B` of segment `AB` is found. Ongoing segment `AB` is closed. We can't take any point after that into account. + +We can sort our queries so that we can calculate answers for them in increasing order as we iterate over the set **S** while only maintaining a single counter. + +That was the general approach. Now, solving the original problems is all about handling the exception case where we may have the same point for many openings and endings. See the code and comments for understading how to handle duplicate points. + +## Complexity +- Time Complexity: O(T * (N * lg(N) + Q * lg(Q)). +- Memory Complexity: O(N + Q). + +## Code + +### C++ + +```cpp +#include + +using namespace std; + +typedef pair pii; + + +int main() { + + // for fast I/O + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + int n, q; + cin >> n >> q; + + vector point, query; + vector ans(q); + for(int i = 0; i < n; ++i) { + int a, b; + cin >> a >> b; + + // 0 -> opening and 1 -> ending + point.push_back({a, 0}); + point.push_back({b, 1}); + } + sort(point.begin(), point.end()); + + for(int i = 0; i < q; ++i) { + int p; + cin >> p; + + // saving each query with their corresponding index + query.push_back({p, i}); + } + sort(query.begin(), query.end()); + + int cnt = 0, idx = 0, accumulator = 0, ending = 0; + for(int i = 0; i < (int)point.size(); ++i) { + if (point[i].second == 0) { + accumulator++; + } + else { + ending++; // segments that have been closed for the later points + } + if (i < (int)point.size() && point[i+1].first == point[i].first) { // accumulating cases with same opening or ending: (p, 0) or (p, 1) + continue; + } + + // case 1: query points strictly less than current point in consideration + while (idx < q && query[idx].first < point[i].first) { + ans[query[idx++].second] = cnt; + } + // case 2: query points equal to current point in consideration + while (idx < q && query[idx].first == point[i].first) { + ans[query[idx++].second] = cnt + accumulator; + } + cnt += accumulator-ending; + accumulator = ending = 0; + } + + // corner case: coming out of loop before the last point being considered + while (idx < q && query[idx].first < point.back().first) { + ans[query[idx++].second] = cnt; + } + while (idx < q && query[idx].first == point.back().first) { + ans[query[idx++].second] = cnt + accumulator; + } + + cout << "Case " << ts << ":\n"; + for(int i = 0; i < q; ++i) { + cout << ans[i] << '\n'; + } + } + + return 0; +} +``` diff --git a/1109/en.md b/1109/en.md index dc8218a3..9e661373 100644 --- a/1109/en.md +++ b/1109/en.md @@ -10,7 +10,7 @@ Note: `d(n)` denotes the number of divisors of the integer `n` One approach to solve the problem is to pre-compute the arrangement and afterward, to print out the answer in `O(1)` time complexity. This can be achieved by: 1. Calculating the number of divisors of the integers using [modified sieve](https://codeforces.com/blog/entry/22229) technique -2. Computing the order of integers with the help of [custom comparison function](http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/) of C++ +2. Computing the order of integers with the help of [custom comparison function](https://www.onlycode.in/custom-comparators-in-c/) of C++ This is a very good problem to help you learn custom comparison in C++. However, some people might find it difficult to get an `Accepted` verdict. The reason could be one of the following: @@ -60,4 +60,4 @@ int main() { } return 0; } -``` \ No newline at end of file +``` diff --git a/1159/en.md b/1159/en.md new file mode 100644 index 00000000..89b5896a --- /dev/null +++ b/1159/en.md @@ -0,0 +1,73 @@ + +# [LOJ 1159 Batman](https://lightoj.com/problem/batman) + +## Solution Approach: +In this problem given 3 string and need to find the length of the longest common subsequence +[LCS](https://www.programiz.com/dsa/longest-common-subsequence) of these three strings. + + + +Let's define LCS[i][j][k] to be the LCS of the three sub-strings X[1..i], Y[1..j], Z[1..k]. + + +To compute the value of LCS[i][j][k], we consider three possibilities: +- You can start by checking base case whether the length of any string is 0, in which case the LCS is 0. +- if last characters of each string match, length of the common subsequence would be 1 plus + the length of the common subsequence till the i-1,j-1 and k-1 indexes +- Otherwise, we can drop the last character of any one of the three strings and take the maximum value i.e. LCS[i][j][k] = max(LCS[i-1][j][k], LCS[i][j-1][k], LCS[i][j][k-1]). + + + +The final LCS value is stored in LCS[len(X)][len(Y)][len(Z)], which is the LCS of the three input strings. + +This problem is similar to [this problem](https://leetcode.com/problems/longest-common-subsequence/description/), you can check that also. + + +# C++ +``` +#include +using namespace std; + + +int LCS( string X, string Y, string Z, int m, int n, int o) +{ + int dp[m+1][n+1][o+1]; + + //Going with bottom up aproach + for (int i=0; i<=m; i++) + { + for (int j=0; j<=n; j++) + { + for (int k=0; k<=o; k++) + { + if (i == 0 || j == 0||k==0) + dp[i][j][k] = 0; + + else if (X[i-1]==Y[j-1] && X[i-1]==Z[k-1]) + dp[i][j][k] = dp[i-1][j-1][k-1] + 1; + + else + dp[i][j][k] =max(max(dp[i-1][j][k], dp[i][j-1][k]), dp[i][j][k-1]); + } + } + } + + return dp[m][n][o]; +} + + +int main() +{ + int len; + cin>>len; + for(int idx=1;idx<=len;idx++) + { + string X,Y,Z; + cin>>X>>Y>>Z; + int m = X.length(); + int n = Y.length(); + int o = Z.length(); + cout << "Case "<< idx<<": "<< LCS(X, Y, Z, m, n, o)< 1) from **ans[k]** so that the **ans[k]** will be the required answer. Having discussed the solution we are bound to calculate the **ans** array in decreasing order as every **ans[i]** depends on the multiples of **i**. + +## Complexity +- Time Complexity: O(T * N * $log{_2}{N}$ ). +- Memory Complexity: O(N). + +## Code + +### C++ + +```cpp +#include + +using namespace std; + + +typedef long long ll; + + +const int MAXN = 1e4; + +inline ll nC4(ll n) { + return (n*(n-1)*(n-2)*(n-3)) / 24; +} + +int main(int argc, const char *argv[]) { + + // for fast I/O + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + int n; + cin >> n; + + vector div_cnt(MAXN+1); + for(int i = 0; i < n; ++i) { + int x; + cin >> x; + + for(int j = 1; j*j <= x; ++j) { + if (x % j) { + continue; + } + div_cnt[j]++; + if (j*j != x) { + div_cnt[x/j]++; + } + } + } + + vector ans(MAXN+1); + for(int i = MAXN; i >= 1; --i) { + ans[i] = nC4(div_cnt[i]); + for(int j = i+i; j <= MAXN; j += i) { + ans[i] -= ans[j]; + } + } + + cout << "Case " << ts << ": " << ans[1] << '\n'; + } + + return 0; +} +``` diff --git a/1164/en.md b/1164/en.md new file mode 100644 index 00000000..bd726b5b --- /dev/null +++ b/1164/en.md @@ -0,0 +1,128 @@ +# LOJ-1164: Horrible Queries + +## Prerequisite: +- [Segment Tree](https://cp-algorithms.com/data_structures/segment_tree.html) +- [Lazy Propagation](https://www.topcoder.com/thrive/articles/range-operations-lazy-propagation) + +## Problem at a glance: +In this problem we are required to perform two kinds of operation. +1. Update a range by a given `value` +2. Find the total sum of given range + +## Approach +Its clear that we can use `cumulative sum` to answer the second type of query. However, updating a range of values using this approach would have a time complexity of **O(n)**, which could lead to a **"Time Limit Exceeded" (TLE)** error for larger inputs. If you are already familiar with **segment tree** then you can start with segment tree. But unlike segment tree we are required to **update range** which may lead to **TLE** To solve this problem we can use a variation of **segment tree** called **lazy propagation** + +## Lazy Propagation at a glance +Lazy propagation is an optimization technique used with segment trees. If you are not familiar with segment trees, it is recommended to learn about them before proceeding. + +With lazy propagation, instead of updating a range instantly, we only mark the root node of that range for future updates. We maintain an extra array to keep track of which nodes need to be updated. Here's an illustration: + +![example image of lazy propagation](lazyPropagationExample.jpg) + +Let's say we want to update the range [4:6]. The root node of this range is the 3rd node. Rather than updating all the nodes below [4:6] immediately, we simply mark the immediate children of the 3rd node with a value that should be added in the future. Our second array tracks this information for each node to determine if its child nodes should be updated or not. When we need to query or update the range again, we will update the specific node. + +You will have a better understanding of this concept as you read through the tutorial. It's a straightforward problem if you can understand both the segment tree and lazy propagation algorithms. + +### Code + +``` +#include + +using namespace std; + + +#define endl "\n" +#define MAX 400010 + + +long long segSum[MAX], lazy[MAX]; + +void clearAll() { + for (int i = 0; i < MAX; i++) { + segSum[i] = 0; + lazy[i] = 0; + } +} + + +void update(int node, long long leftTree, long long rightTree, long long left, long long right, long long value) { + int leftSide = node * 2; + int rightSide = (node * 2) + 1; + if (lazy[node]) { + segSum[node] += ((rightTree - leftTree + 1) * lazy[node]); + if (leftTree != rightTree) { + lazy[leftSide] += lazy[node]; + lazy[rightSide] += lazy[node]; + } + lazy[node] = 0; + } + if (leftTree > right || rightTree < left) { + return; + } + if (leftTree >= left && rightTree <= right) { + segSum[node] += ((rightTree - leftTree + 1) * value); + if (leftTree != rightTree) { + lazy[leftSide] += value; + lazy[rightSide] += value; + } + return; + } + int mid = (leftTree + rightTree) >> 1; + update(leftSide, leftTree, mid, left, right, value); + update(rightSide, mid + 1, rightTree, left, right, value); + segSum[node] = segSum[leftSide] + segSum[rightSide]; +} + + +long long query(int node, long long lt, long long rt, long long l, long long r) { + int leftSide = node * 2; + int rightSide = (node * 2) + 1; + if (lazy[node]) { + segSum[node] += ((rt - lt + 1) * lazy[node]); + if (lt != rt) { + lazy[leftSide] += lazy[node]; + lazy[rightSide] += lazy[node]; + } + lazy[node] = 0; + } + if (lt > r || rt < l) { + return 0; + } + if (l <= lt && rt <= r) { + return segSum[node]; + } + int mid = (lt + rt) >> 1; + long long leftSum = query(leftSide, lt, mid, l, r); + long long rightSum = query(rightSide, mid + 1, rt, l, r); + return leftSum + rightSum; +} + + +int main() { + int t, cs = 1; + cin >> t; + while (t--) { + int n,q; + cin >> n >> q; + clearAll(); + cout <<"Case "<< cs++ << ":\n"; + while(q--) { + int choice, left, right; + cin >> choice >> left >> right; + if (choice == 0) { + long long value; + cin >> value; + update(1, 0, n - 1, left, right, value); + } + else { + cout << query(1, 0, n - 1, left, right) < +using namespace std; + +vector vis; +vector dis_from_s, dis_from_d; +vector > graph; + +void bfs(int node, vector& dis) +{ + queue q; + vis[node] = true; + q.push(node); + + while(!q.empty()) + { + int u = q.front(); + q.pop(); + + for(auto v: graph[u]) + { + if(!vis[v]) + { + vis[v] = true; + dis[v] = dis[u]+1; + q.push(v); + } + } + } +} + +int main() +{ + ios_base::sync_with_stdio(0); + cin.tie(0); + cout.tie(0); + + int t; + cin>>t; + + for(int cs=1; cs<=t; cs++) + { + int n, r, s, d, ans = 0; + cin>>n>>r; + vis.assign(n, false); + dis_from_s.assign(n, 0); + dis_from_d.assign(n, 0); + graph.assign(n, vector()); + + for(int i=0; i>u>>v; + graph[u].push_back(v); + graph[v].push_back(u); + } + + cin>>s>>d; + bfs(s, dis_from_s); + fill(vis.begin(), vis.end(), false); + bfs(d, dis_from_d); + + for(int i=0; i + +using namespace std; + +const int INF = numeric_limits :: max(); // Indicating Unreachable state + +int r, c; +vector grid; + +// An Efficient (and quite common) Way to Navigate Grid Problems: https://codeforces.com/blog/entry/78827 +const int dr[] = {-1, 0, 1, 0}; +const int dc[] = {0, 1, 0, -1}; + +inline bool valid(int x, int y) { + // 0-based index grid + return x >= 0 && x < r && y >= 0 && y < c && grid[x][y] != '#'; +} + +struct Cell { + bool flag; // Indicating whther this cell is on fire or not + int x, y; // Row and column respectively + + Cell() {} // Default constrcutor + Cell(bool flag, int x, int y) : flag(flag), x(x), y(y) {} +}; + +int main() { + + // For fast I/O + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + cin >> r >> c; + + grid.resize(r); + + pair source; + queue Q; + // dist[i][j] = Minimum minutes needed to move to cell[i][j] from starting cell + vector > dist(r, vector (c, INF)); + for(int i = 0; i < r; ++i) { + cin >> grid[i]; + for(int j = 0; j < c; ++j) { + if (grid[i][j] == 'J') { + source = {i, j}; + } + else if (grid[i][j] == 'F') { + // Turning into a movable obstacle + grid[i][j] = '#'; + Q.push(Cell(true, i, j)); + } + } + } + + // All the fire cells have been added to the queue so that they always move to the adjacent cells first not, Jane + dist[source.first][source.second] = 0; + Q.push(Cell(false, source.first, source.second)); + + while (!Q.empty()) { + Cell u = Q.front(); + Q.pop(); + + for(int i = 0; i < 4; ++i) { + int x = u.x + dr[i]; + int y = u.y + dc[i]; + + // Checking for not an obstacle yet + if (valid(x, y) && dist[x][y] == INF) { + if (u.flag) { + // Now an obstacle that can move + grid[x][y] = '#'; + Q.push(Cell(true, x, y)); + } + else { + // That's jane moving + dist[x][y] = dist[u.x][u.y] + 1; + Q.push(Cell(false, x, y)); + } + } + } + } + + int best = INF; + // Topmost and downmost rows + for(int j = 0; j < c; ++j) { + best = min({best, dist[0][j], dist[r-1][j]}); + } + // Leftmost and rightmost columns + for(int i = 0; i < r; ++i) { + best = min({best, dist[i][0], dist[i][c-1]}); + } + + cout << "Case " << ts << ": "; + + if (best == INF) { + cout << "IMPOSSIBLE\n"; + } + else { + // Adding 1 because of getting out of the maze completely + cout << best+1 << '\n'; + } + + grid.clear(); + } + + return 0; +} +``` diff --git a/1183/en.md b/1183/en.md new file mode 100644 index 00000000..3b3eb91c --- /dev/null +++ b/1183/en.md @@ -0,0 +1,133 @@ +# LOJ 1183 - Computing Fast Average + +## Summary +The problem requires implementing two types of queries on an array of integers (initially all values are 0): + +1. Changing the value of elements in a range of indices with a specific value given. (**update query**) + +2. Finding the average value of integers in a range of indices. (**get query**) + +## Prerequisite +Segment tree with lazy propagation: + +https://cp-algorithms.com/data_structures/segment_tree.html#find-the-smallest-number-greater-or-equal-to-a-specified-number-acceleration-with-fractional-cascading + +(See the section titled **Range updates (Lazy Propagation)**) + +## Solution +The solution is nothing but formulating it to the **range update-sum query**. The range update query is equivalent to the classic lazy update (setting a value to a range of indices) so is the range sum query. To form an irreducible fraction we divide both the nominator (range sum) and the denominator (total indices) by the gcd of them. + +## Complexity +- Time Complexity: O(T * (N + Q * $log{_2}{N}$)). +- Memory Complexity: O(N). + +## Code + +### C++ + +```cpp +#include + +using namespace std; + + +struct SegmentTree { + int n; + vector tree, lazy; + + SegmentTree(int n) : n(n) { + tree.assign(n<<2, 0); + lazy.assign(n<<2, -1); + } + + void propagate(int now, int l, int r) { + int left = now<<1; + int right = left|1; + + int mid = (l+r)>>1; + + tree[left] = (mid-l+1) * lazy[now]; + tree[right] = (r-mid) * lazy[now]; + lazy[left] = lazy[right] = lazy[now]; + lazy[now] = -1; + } + + void update(int now, int l, int r, const int i, const int j, const int val) { + if (i > r || j < l) return ; + if (i <= l && j >= r) { + tree[now] = (r-l+1) * val; + lazy[now] = val; + return ; + } + + int mid = (l+r)>>1; + int left = now<<1; + int right = left|1; + + if (lazy[now] != -1) propagate(now, l, r); + + update(left, l, mid, i, j, val); + update(right, mid+1, r, i, j, val); + + tree[now] = tree[left] + tree[right]; + } + + int query(int now, int l, int r, const int i, const int j) { + if (i > r || j < l) return 0; + if (i <= l && j >= r) return tree[now]; + + int mid = (l+r)>>1; + int left = now<<1; + int right = left|1; + + if (lazy[now] != -1) propagate(now, l, r); + + return query(left, l, mid, i, j) + query(right, mid+1, r, i, j); + } +}; + +int main() { + + // for fast I/O + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + int n, q; + cin >> n >> q; + + SegmentTree seg_tree(n); + + cout << "Case " << ts << ":\n"; + while (q--) { + int type, i, j; + cin >> type >> i >> j; + + // 1-indexing + ++i; + ++j; + + if (type == 1) { + int v; + cin >> v; + seg_tree.update(1, 1, n, i, j, v); + } + else { + int nominator = seg_tree.query(1, 1, n, i, j); + int denominator = j-i+1; + int gcd = __gcd(nominator, denominator); + nominator /= gcd; + denominator /= gcd; + + if (denominator == 1) cout << nominator << '\n'; + else cout << nominator << '/' << denominator << '\n'; + } + } + } + + return 0; +} +``` \ No newline at end of file diff --git a/1231/en.md b/1231/en.md new file mode 100644 index 00000000..6f5691da --- /dev/null +++ b/1231/en.md @@ -0,0 +1,101 @@ +## Understanding The Problem Statement + +First we identify what the problem wants, we have n coin denomination or values Ai where for my approach we take i as 0<=i<=n +and n has the constraint 1<=n<=50, and each of the coin with value A is given a count or the number of these coins we have, which is Ci. We are also provided +K which is the value we wish to pay off using a valid coin combination. + +The question simply asks us to find the number of ways by which we can pay off the required value using our available coins. + +## Prerequisites + +- Basic Understanding of DP(Dynamic Programming) [DP-1 Jenny CS IT](https://youtu.be/lVR2u9lsxl8) +- Preliminary Idea of Coin Change Problem [DP#2 Jenny CS IT](https://youtu.be/L27_JpN6Z1Q) + +## Solution Approach + +This problem is essentially a variation of the classic dynamic programming problem, coin change. Here we first recognize that we need to store the values for the coin denominations +and the number of each of these coin types in two seperate arrays. Then we define the following formal definition of a recursive function: + +coinchange(index,current_value): returns the number of ways by which we can pay off the target cost with the given coin denominations, and number of coins of each type + +coinchange(index,current_value): { + 1 if current_value=0 + 0 if i=n(since we are using 0-based indexing) + coinchangeix(i+1,current_value-ix\*Ai) if 0<=ix<=C[i] and 0<=i +using namespace std; +#define ll long long +#define INF 100000009 +#define modulo 100000007 +#define Max_n 55 +#define Max_K 1005 +#define Max_C 25 +int n; +ll K; +vectorA,C; + +int dp[Max_n][Max_K]; + +ll coinchange(int i,int current_value){ + if(current_value==0){ + return 1; + } + if(i==n){ + return 0; + } + if(dp[i][current_value]!=-1){ + return dp[i][current_value]; + } + dp[i][current_value]=0; + for(int ix=0;ix<=C[i] && current_value-ix*A[i]>=0;ix++){ + dp[i][current_value]+=(coinchange(i+1,current_value-ix*A[i])%modulo); + } + return dp[i][current_value]%modulo; + +} + + +void run_test_case(){ + memset(dp,-1,sizeof(dp)); + cin>>n>>K; + A.resize(n); + C.resize(n); + for(ll &i:A){ + cin>>i; + } + for(ll &i:C){ + cin>>i; + } + cout<>number_of_test_cases; + for(int current_case=1;current_case<=number_of_test_cases;current_case++){ + cout<<"Case "< + +using namespace std; + + +int main() { + + // For fast I/O + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + const int MAXN = 1e8; + + int t; + cin >> t; + + // 1-based indexing + vector > N(t+1); + for(int i = 1; i <= t; ++i) { + cin >> N[i].first; + // Saving corresponding index of the test case + N[i].second = i; + } + sort(N.begin(), N.end()); + + vector ans(t+1); + double now = 0; + int idx = 1; + for(int i = 1; i <= MAXN; ++i) { + now += 1.0 / i; + // Could be duplicates, that's why using 'while' loop + while (idx < (int)N.size() && N[idx].first == i) { + ans[N[idx++].second] = now; + } + } + + for(int ts = 1; ts <= t; ++ts) { + cout << fixed << setprecision(10) << "Case " << ts << ": " << ans[ts] << '\n'; + } + + return 0; +} +``` diff --git a/1241/en.md b/1241/en.md new file mode 100644 index 00000000..62aef78b --- /dev/null +++ b/1241/en.md @@ -0,0 +1,51 @@ +# LOJ 1389 - Scarecrow + +### Summary: + +First, you will be given an integer __T (1 ≤ T ≤ 100)__ denoting the number of test cases. Each case will start with a line containing an integer __n (1 ≤ n ≤ 10)__, where __n__ denotes the total number of reports of the size of Pinocchio's nose in a certain day. The next line contains __n__ space separated integers: __a1, a2, ..., an__ denoting the sizes of his nose in that day. All the values of __ai (1 ≤ i ≤ n)__ will be between 2 and 50. + +### Idea: + + We have to find the minimum number of lies Pinocchio has told. When he tells a lie, his nose grows at least 1 cm and at most 5 cm. So assuming his nose grows 5 cm for most of the lies he tells will minimize our answer. For example, if his nose grows 13 cm in a certain period of time, then the minimum number of lies will be 3. + + As it is assumed that he hasn't told any truth, the given sizes will be in non-decreasing order. We will use __a0__ = 2 (initial size of his nose). From the __i-1__-th timestamp to the i-th, Pinocchio has told minimum ceil((ai - ai-1) / 5) lies. So using a loop we will add this value with a counter variable for each __i__ from 1 to __n__ and finally we will get our result. + +### Code: +### C++ +----- +```cpp +#include +using namespace std; +int main() +{ + ios_base::sync_with_stdio(0); + cin.tie(0); + cout.tie(0); + + int t; + cin>>t; + + for(int cs=1; cs<=t; cs++) { + int n, cnt; + cin>>n; + vector vec = {2}; /// initialing the vector with the initial size of Pinocchio's nose + cnt = 0; + + for(int i=1; i<=n; i++) { + int x; + cin>>x; + vec.push_back(x); + } + + for(int i=1; i +![first](./first.PNG) +- In the first query, we have to find how many point are inside (1,1) and (6,6). We can easily do that by querying the BIT, the ans is 2. +- Let's come to the second query. Here we have to find the number of point inside (2,2) and (5,5). We will call query function with parameter (5,5). But it will return the number of point inside (1,1) and (5,5). The area is ploted bellow in red- + ![second](./second.PNG) +- We took some extra area. Because we have to find from (2,2) to (5,5) not (1,1) to (5,5). However, the final equation of ans is -
+`Ans=A-B-C+D` where + * A = Number of points in rectangle (1, 1) to (x2,y2) + * B = Number of points in rectangle (1, 1) to (x1-1,y2-1) + * C = Number of points in rectangle (1, 1) to (x2-1,y1-1) + * D = Number of points in rectangle (1, 1) to (x1-1,y1-1) +let's clarify this equation. First look at the area ploted bellow of **A,B,C,D**. + ![third](./third.PNG) + ![fourth](./fourth.PNG) +- As D is inside of both B and C. So at the time of substracting B and C , D is subtracted twice . so D is added. Now after calculating +we got our area where no point is located. So ans of the second query is 0. Hope you understood the solution. + +### Code + +#### C++ +```cpp +#include +using namespace std; +long long int BIT[1005][1005]; +bool vis[1005][1005]; + +void update(int x, int y , int val) +{ + while(x<=1001) + { + int y1=y; + while(y1<=1001) + { + BIT[x][y1]+=val; + y1+=(y1&-y1); + } + x+=(x&-x); + } +} +long long int query(int x, int y) +{ + long long int sum=0; + while(x>0) + { + int y1=y; + while(y1>0) + { + sum+=BIT[x][y1]; + y1-=(y1&-y1); + } + x-=(x&-x); + } + return sum; +} +void solve() +{ + + memset(BIT,0,sizeof(BIT)); //initialize with zero + memset(vis,false,sizeof(vis)); // initalize with zero + long long int q,a,b,c,d,x1,y1,x2,y2; + cin>>q; + while(q--) + { + cin>>a; + if(a==0) + { + cin>>a>>b; + a++,b++; + if(!vis[a][b]) + { + vis[a][b]=true; + update(a,b,1); //update the index + } + } + else + { + cin>>x1>>y1>>x2>>y2; + x1++,x2++,y1++,y2++; + // find total point inside the given rectangle + long long int ans=query(x2,y2)-query(x2,y1-1)-query(x1-1,y2)+query(x1-1,y1-1); + cout<>tt; + for(int i=1;i<=tt;i++) + { + cout<<"Case "< +#define N ((int)1e5 + 5) + +using namespace std; + +int A[N]; +int contribution[N]; + +int main() +{ + ios_base::sync_with_stdio(false); + cin.tie(NULL); + + int t; + cin>>t; + for(int i = 1; i <= t; i++) { + int n, q; + cin>>n>>q; + + long long sum = 0; + + for(int j = 0; j < n; j++) { + cin>>A[j]; + contribution[j] = (n-j-1) - j; + sum += 1LL * A[j] * contribution[j]; + // multiplying by 1LL is important because A[j] * contribution[j] + // can be greater than INT_MAX (2^31 - 1) + } + + cout << "Case " << i << ":\n"; + while(q--) { + int type; + cin>>type; + if(type == 0) { + int idx, val; + cin>>idx>>val; + sum -= 1LL * A[idx] * contribution[idx]; + A[idx] = val; + sum += 1LL * A[idx] * contribution[idx]; + } else { + cout << sum << "\n"; + } + } + } +} +``` diff --git a/1370/en.md b/1370/en.md new file mode 100644 index 00000000..05e74657 --- /dev/null +++ b/1370/en.md @@ -0,0 +1,74 @@ +# LOJ 1370 - Bi-shoe and Phi-shoe + +In this problem, you will be given `T` testcases. The first line of each test case contains a single integer `n` where `n` denotes the number of students of Phi-Shoe who is a massively popular coach. + +The next line contains n space separated integers denoting the lucky numbers for the students through which assistant of Phi-Shoe,Bi-Shoe can help students in buying bamboos.Now, in the problems statement it was told to design an algorithm through which minimum amount of money will be spent for buying bamboos for the students. + + +### Approach: +In order to solve this problem, there is a prerequisite that need to be fulfilled which is: you need to have a basic understanding of Sieve of Eratosthenes.The tutorials are given below in the "Resources" section. + +So having a clear concept of the topic mentioned above, we can solve this problem. + +In the problem we are given, ```Score of a bamboo = Φ (bamboo's length)``` where score of the bamboos denote the lucky numbers we found in the second line of each testcase.By ```Φ(n)``` it was meant count of numbers less than n that are relatively prime(having no comoon divisor other than 1) to n. +It was given that cost of one unit of bamboo is 1 Xukha(a form of currency) and each student has to buy one bamboo.Now,since our goal is to minimize total cost,so our goal would be to buy the smallest bamboo possible for each student. + +Students can buy a bamboo with a score greater than or equal to his/her lucky number. So our approach would be to find the least sized length of bamboos whose score is atleast equal to the lucky numbers and we can find out the least sized length of bamboo through finding out the next immediate prime number present from the lucky number. There are a few reasons for making such a claim.The reasons are: + +- Bamboo size will always be greater than the lucky number for each student. +- For a lucky number of 8, there are no numbers smaller than 11 that have a count of relatively prime number of at least 8. So our score would be 10(1,2,3,4,5,6,7,8,9,10) here which is a greater value than the lucky number. So we have to take a bamboo size of 11 for the student having a lucky number of 8. + +### Resources: +Here are some resources of **Sieve of Eratosthenes** so that you can have a deeper understanding on this topic and learn to solve problems from this topic using these resources: +- [CP-Algorithm](https://cp-algorithms.com/algebra/sieve-of-eratosthenes.html#segmented-sieve) +- [Shafayet's Blog](https://www.shafaetsplanet.com/?p=624) + +If you are still stuck with this problem, check the code below: + +### C++ +```cpp +#include +using namespace std; +bitset<6000008> mark; +void sieve() +{ + mark[0] = mark[1] = 1; + for (int i = 4; i <= 4e6; i += 2) + mark[i] = 1; + for (int i = 3; i * i <= 4e6; i += 2) + { + if (!mark[i]) + { + for (int j = i * i; j <= 4e6; j += 2 * i) + mark[j] = 1; + } + } +} +int main() +{ + sieve(); + int t; + cin >> t; + for (int k = 1; k <= t; k++) + { + int n; + cin >> n; + int ans = 0; + for (int i = 0; i < n; i++) + { + int num; + cin >> num; + num++; + for (int j = num;; j++) + { + if (mark[j] == 0) + { + ans += j; + break; + } + } + } + cout << "Case " << k << ": " << ans << " " << "Xukha" << endl; + } +} +``` diff --git a/1389/en.md b/1389/en.md new file mode 100644 index 00000000..fdbba1e1 --- /dev/null +++ b/1389/en.md @@ -0,0 +1,45 @@ +# LOJ 1389 - Scarecrow + +In this problem you will be given __T__ test cases. Each case starts with a line containing an integer __N (0 < __N__ < 100)__. The next line contains __N__ characters that describe the __1 x N__ rectangular field. A dot(__.__) indicates a crop-growing spot and a hash(__#__) indicates an infertile region. For each case, you have to print the case number and the minimum number of scarecrows that need to be used. + +### Observation and Solution: + +This is a straight forward greedy problem. A scarecrow, when placed on a spot, covers the cell to its immediate left and right along with the cell it is on. So basically it covers three consecutive cells. Hence the efficient approach would be traversing the field from either left or right(the code given below traverses from left) and once we find an uncovered crop-growing cell(a dot), we will need a scarecrow to cover that cell and the next two cells(if exist) will be automatically covered(no matter what type of cells they are, dots or hashes) by that scarecrow. So we can skip iteration for the next two cells of a covered crop-growing cell(a dot). We will count the number of scarecrows needed throughout this process and after traversing the whole field, the number of scarecrows needed will be our desired result. + +### Code: +### C++ +----- +```cpp +#include +using namespace std; +int main() +{ + ios_base::sync_with_stdio(0); + cin.tie(0); + cout.tie(0); + + int t; + cin>>t; + + for(int cs = 1; cs <= t; cs++) { + int n, cnt; + string field; + cin>>n; + cin>>field; + cnt = 0; + + for(int i = 0; i < n; i++) { + if(field[i] == '.') { + i += 2; + cnt++; + } + } + + cout<<"Case "< +using namespace std; +#define N 9 + +int grid[N][N]; + +bool isSafeInRow(int& row,int value){ + for(int i=0;i>t; + cin.ignore(); + for(int i=1;i<=t;i++){ + cout<<"Case "< + +using namespace std; + +typedef long long ll; + + +int main(int argc, const char *argv[]) { + + // for fast IO + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int t; + cin >> t; + + for(int ts = 1; ts <= t; ++ts) { + ll n; + cin >> n; + + vector ans {1}; + for(ll i = 2; i*i <= n; ++i) { + int cnt = 0; + while (n % i == 0) { + cnt++; + n /= i; + } + + bool flag = false; // first backward iteration because of satisfying the contraint. For first prime number it doesn't matter. + int x = i; + int current_size = ans.size(); // size of the divisors list not containing the prime number i + while (cnt > 0) { + if (flag) { + // forward iteration + for(int i = 0; i < current_size; ++i) ans.push_back(ans[i] * x); + } + else { + // backward iteration + for(int i = current_size-1; i >= 0; --i) ans.push_back(ans[i] * x); + } + flag ^= true; + x *= i; // raising to the next power of that prime number + cnt--; + } + } + + if (n > 1) { + // a single prime factor greater than square root of that number + for(int i = ans.size()-1; i >= 0; --i) ans.push_back(n * ans[i]); + } + + cout << "Case " << ts << ":\n"; + for(ll x : ans) { + cout << x << ' '; + } + cout << '\n'; + } + + return 0; +} +``` diff --git a/1509/en.md b/1509/en.md new file mode 100644 index 00000000..5e7d0ca8 --- /dev/null +++ b/1509/en.md @@ -0,0 +1,35 @@ +# LOJ 1509 - ICPC Standing + +In this problem, you will be given `T` testcases. The first line of each test case contains three integers `P` ,`S` and `R` where `P` denotes the number of problems in the problem set, `S` denotes the number of solved problems by the current team and `R` denotes the rank of the current team. +Now, in the problems statement it was asked if there was a chance for the current team to become champion after the end of the contest. + + +### Approach: +This is a very simple adhoc problem.The current team will only not be able to become the champion of the contest if they have solved all the problems that are in the problem statement and still couldn't achieve the first position.Other than this there is always a chance for the current team to become champion after the end of the contest. + +If you are still stuck with this problem, check the code below: + +### C++ +```cpp +#include +using namespace std; +int main() +{ + int t; + cin >> t; + for (int k = 1; k <= t; k++) + { + int p, s, r; + cin >> p >> s >> r; + cout << "Case " << k << ": "; + if (p == s and r != 1) + { + cout << "No" << endl; + } + else + { + cout << "Yes" << endl; + } + } +} +``` diff --git a/README.md b/README.md index 89fff976..8c59b329 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ an issue first like this: https://github.com/lightoj-dev/problem-tutorials/issue We are planning to have tutorials both in Bangla and English language. But you don't need to work on both, write the English one first, then maybe you or other contributor can translate it to Bangla. -Each problem will have its own folder with the name of its problem id, such as 1000, 1214. Each folder will contain tutorial as `en.md` file for English and `bn.md` for Bangla language. +Each problem will have its own folder with the name of its problem id, such as `1000`, `1214`, for DimikOJ problems it will be the problem handle such as `dimik-even-odd-1`, `dimik-even-odd-2`. Each folder will contain tutorial as `en.md` file for English and `bn.md` for Bangla language. Each tutorial should explain what is the problem is asking (maybe in simpler way than the problem description), what algorithm is required to solve the problem, why we need to use that algorithm, what other options out there to solve this problem. You don't need to give idea about all the different algorithms, you can just explain one way of solving the problem. Feel free to add any image if you think appropriate. Also include any other 3rd party article, book link so that other problem solver can learn from there. If you already shared the tutorial on other website (maybe your personal blog site), you can mention those link on the tutorial. And last but not the least, add your code on the tutorial. Please properly indent your code, also add comments on your code to make code more readable to problem solvers. diff --git a/dimik-box-1/en.md b/dimik-box-1/en.md new file mode 100644 index 00000000..e2b15b8c --- /dev/null +++ b/dimik-box-1/en.md @@ -0,0 +1,29 @@ +# Dimik - Box 1 + +In this problem, you will be given `T` testcases. Each line of the testcase consists of an integer `n`. We just have to print `*` in `n` rows and `n` columns. + +### Solution +We can find the solution by running two nested loops. We run the outermost loop for each row and for each row we run the innermost loop for each column. The thing to observe here is we print an empty line between consecutive test cases. So there will be no empty lines after last test case. + +### C++ +```cpp +#include +using namespace std; +int main() +{ + int t; + cin >> t; + for (int k = 1; k <= t; k++) + { + int n; + cin >> n; + for (int i = 1; i <= n; i++) + { + for (int j = 1; j <= n; j++) + cout << "*"; + cout << endl; + } + if(k!=t) cout << endl; + } +} +``` diff --git a/dimik-divisor/en.md b/dimik-divisor/en.md new file mode 100644 index 00000000..b7fe8ff4 --- /dev/null +++ b/dimik-divisor/en.md @@ -0,0 +1,74 @@ +## Dimik Divisor + +### What the problem wants + +Basically you will be given a number you need to find all the divisor from it + +You can find more about divisors [here](https://www.splashlearn.com/math-vocabulary/division/divisor) + +### Solution + +The most basic way to solve this is as following + +- Start a loop from 1 to N , Where N is the number given +- Check if the any number can divide N with a remainder of 0 +``` 6 % 3 == 0 ``` That means the remainder of 6 after dividing it by 3 is 0 +- You can print them as they are the divisor +> Be careful about newline and extra space. If you are stuck with that problem see the code to know how to handle it + +### CPP Code + +```cpp + +#include // includes necessary library +using namespace std; +#define ll long long int +int main() +{ + int test; + cin>>test; + for(int t=1;t<=test;t++) + { + int num; + cin>>num; + cout<<"Case "< +using namespace std; +int main() +{ + int t; + cin >> t; + for (int k = 1; k <= t; k++) + { + int n; + cin >> n; + if (n % 2) + cout << "odd" << endl; + else + cout << "even" << endl; + } +} +``` diff --git a/dimik-even-odd-2/en.md b/dimik-even-odd-2/en.md new file mode 100644 index 00000000..d0f60c99 --- /dev/null +++ b/dimik-even-odd-2/en.md @@ -0,0 +1,109 @@ +## Dimik Even or Odd 2 + +### What the problem Wants +In this problem, you will be given `T` testcases.Each line of the testcase consist of an integer `n`.We have to determine whether `n` is even or odd. The difference from dimik even odd 1 is that the number can have upto 100 digits which won't fit in long long data type of C++. + +### Approach +- Take Input as a string so that it can take upto 100 digits +- Check the last digit of that string +- If its divisible by 2 then print 'even' otherwise print 'odd' + +### Solution in C++ + +> Step 1: Include necessary libraries + +```cpp +#include +using namespace std; +``` + +The bits/stdc++.h library is included, which is a header file that includes all the standard libraries in C++. + +### Define the function to check if a number is even + +```cpp +bool isEven(string number) { + int lastDigit = number[number.size() - 1] - '0'; + return (lastDigit % 2 == 0); +} +``` + +> Step 2: Function for identify Odd Even + +- Inside the isEven function, we extract the last digit of the number by accessing the last character of the string (number[number.size() - 1]) and subtracting the ASCII value of '0' from it. This converts the character representation of the digit to an integer. + +- We then check if the last digit is divisible by 2 using the modulo operator (%). If the remainder is 0, the number is even and we return true; otherwise, we return false + +> Step 3: Use the function from main + +### Implement the main function +```cpp +int main() { + int numTestCases; + cin >> numTestCases; + cin.ignore(); // Ignore the newline character after numTestCases + + for (int i = 0; i < numTestCases; i++) { + string number; + getline(cin, number); + if (isEven(number)) { + cout << "even" << endl; + } else { + cout << "odd" << endl; + } + } + + return 0; +} +``` + +- In the main function, we first read the number of test cases from the input using cin >> numTestCases. + +- Since we read an integer before reading strings, we need to ignore the newline character left in the input buffer. We do this using cin.ignore(). + +- Next, we enter a loop to process each test case. For each test case, we read the number as a string using getline(cin, number). This allows us to handle numbers with multiple digits. + +- We then call the isEven function with the number as the argument. Depending on the return value, we output whether the number is even or odd using cout. + +### Full code +```cpp +#include +using namespace std; + +bool isEven(string number) { + int lastDigit = number[number.size() - 1] - '0'; + return (lastDigit % 2 == 0); +} + +int main() { + int numTestCases; + cin >> numTestCases; + cin.ignore(); // Ignore the newline character after numTestCases + + for (int i = 0; i < numTestCases; i++) { + string number; + getline(cin, number); + if (isEven(number)) { + cout << "even" << endl; + } else { + cout << "odd" << endl; + } + } + + return 0; +} +``` + +### Solution in python + +As python can take upto 100 digit as input we will just check if its even or odd by just dividing it by 2 + +```py +testCase=eval(input()) # taking test case and eval converting it to int +for i in range(testCase): # for each test case + num=eval(input()) # take the numnber and convert it to int + if(num%2==0): # check even or odd + print('even') + else: + print('odd') +``` \ No newline at end of file diff --git a/dimik-factorial/en.md b/dimik-factorial/en.md new file mode 100644 index 00000000..fae1492c --- /dev/null +++ b/dimik-factorial/en.md @@ -0,0 +1,31 @@ +# Dimik - Factorial + +In this problem, you will be given `T` testcases. Each line of the testcase consists of an integer `n`. We have to determine the factorial value of `n` + +### Solution +Factorial of any value n denotes finding the product of all the values starting from 1 upto n. +In other words, `Factorial[n]=1*2*3*4...*(n-2)*(n-1)*n`. +So we can make an efficient solution, by preprocessing all the factorials for different possible values of n and while going through the test cases,we can just print out the factorial of `n`. + +### C++ +```cpp +#include +using namespace std; +int main() +{ + int fact[16]; + fact[0] = 1; + for (int i = 1; i <= 15; i++) + { + fact[i] = i * fact[i - 1]; + } + int t; + cin >> t; + while (t--) + { + int n; + cin >> n; + cout << fact[n] << endl; + } +} +``` diff --git a/interactive-search/en.md b/interactive-search/en.md new file mode 100644 index 00000000..deaadb3a --- /dev/null +++ b/interactive-search/en.md @@ -0,0 +1,88 @@ + # Interactive Search + + ## Problem + This is an easy interactive problem . You will be a value `n` and you have to find the secret number which is within `1` to `n` in `30` tries . You will be given a response `high` or `low` or `correct` . If you guess the number in `30` tries you will get `AC` otherwise `WA` . + + ## Solution + + The maximum value of `n` is `1e9` so we can't use linear search . + + As the number of tries is `30` we can use binary search to find the number because to find a number within a range of `1` to `1e9` it will take binary search maximum `log2(1e9)` which is okay for our problem. + + + So we will use binary search to find the number . + ``` + 1. We will take the input of `n` and set the `low` as `1` and `high` as `n`. + 2. Then we will find the mid value and print it as guess . + 3. Then we will take the response as input . + 5. If the response is `high` then we will set `high` as `mid - 1` . + 6. If the response is `low` then we will set `low` as `mid + 1` . + 7. If the response is `correct` then we will return . + 8. If the count is greater than `30` then we will print `too many tries` and return . +``` + +You can learn more about binary search from +1. [geeksforgeeks](https://www.geeksforgeeks.org/binary-search/) +2. [cp-algorithms](https://cp-algorithms.com/num_methods/binary_search.html) + +``` + Time Complexity : O(log(n)) + Space Complexity : O(1) +``` + + + + + ## CPP +```cpp + +#include + +using namespace std; + +int main() +{ + + int low = 1, high = 100000; + // Taking the value of n as input and setting it as high as it will be the maximum value + cin >> high; + + + // To keep track of the number of tries + int count = 0; + + // Binary search + while (low <= high) + { + // Finding the mid value + int mid = (low + high) >> 1; + // Printing the guess + cout << "guess " << mid << endl; + + + // Taking the response as input + string response; + cin >> response; + + // Checking the response + if (response == "correct") + return 0; + else if (response == "high") + high = mid - 1; + else if (response == "low") + low = mid + 1; + + // Increasing the count + count++; + + // If the count is greater than 30 then printing too many tries and returning + if (count >= 30) + { + cout << "too many tries" << endl; + return 0; + } + } + + return 0; +} +``` diff --git a/ioi-2022-prison/en.md b/ioi-2022-prison/en.md new file mode 100644 index 00000000..fbcbaf25 --- /dev/null +++ b/ioi-2022-prison/en.md @@ -0,0 +1,58 @@ +This problem is about generating a finite state machine to compare two different integers, $A$ and $B$, with the minimum number of states. + +### Subtask 1 + +In this subtask, we can choose $x = N$. We can use the following strategy: + +* The first prisoner will always read 0 on the whiteboard, and then overwrites it with the number of coins in bag A. +* The second prisoner will read a positive integer which represents the number of coins in bag A. So, the second prisoner has to check bag B and then answer which bag has fewer coins. + +### Subtask 2 + +Let $S = {\lceil {\sqrt{N}} \rceil}$. In this subtask, we can do the similar thing in subtask 1 but with 2 phases instead of 1 phase. First, we can compare ${\lfloor {\frac{A}{S}} \rfloor}$ and ${\lfloor {\frac{B}{S}} \rfloor}$. If the values are the same, then we can continue to compare ($A$ mod $S$) and ($B$ mod $S$). This solution only needs $x = 2 \times S$. + +There’s also another solution that needs $x = 3 \times S$ using the fact that every non-negative number can be represented uniquely as $p^2 + q$ for non-negative numbers $p, q$ such that $q < 2 × p + 1$. First, we can compare ${\lfloor {\sqrt{A}} \rfloor}$ and ${\lfloor {\sqrt{B}} \rfloor}$. Then, if the values are the same, we can compare $A - {\lfloor {\sqrt{A}} \rfloor}^2$ and $B - {\lfloor {\sqrt{B}} \rfloor}^2$. + +### Subtask 3 + +In this subtask, we can get some partial scores depending on how small the chosen $x$ is. The following are some possible solutions that could get some points from this subtask. + +#### 3.1 Solution with $x = 3 \times {\lceil {\log_2{N}} \rceil} − 1 = 38$ + +We can simulate how to compare two binary numbers. By noting that it requires up to 13 bits to represent a number up to 5000, the value on the whiteboard represents the current state, i.e. + +* If the value is $3 \times d$, then we need to check the $(12 − d)$-th bit of $A$, and then overwrite it with $3 × d + 1$ or $3 × d + 2$ depending on the bit value. +* If the value is $3 × d + 1$ or $3 × d + 2$, then we know the $(12 − d)$-th bit of $A$ is either 0 or 1 from the previous prisoner. Then, the current prisoner has to check the $(12 − d)$-th of $B$ and compare the current bit value between $A$ and $B$. If the values are still the same, the prisoner can overwrite it with $3 \times (d + 1)$ to continue to the next bit. Otherwise, the prisoner can answers which bag has fewer coins. + +Therefore, this solution needs $x = 3 \times {\lceil {\log_2{N}} \rceil} − 1$ or $x = 38$ for $N = 5000$. + +#### 3.2 Solution with $x = 2 \times {\lceil {\log_2{N}} \rceil} = 26$ + +Instead of storing the bit only from $A$ on the whiteboard, we can store the bit of either $A$ or $B$ alternately. We can use the parity of the current bit position $i$ to determine whether we are storing the $i$-th bit of $A$ or $B$. Therefore, we don't need an additional state for when we have not stored any value of $i$-th bit. + +#### 3.3 Solution with $x = 3 \times {\lceil {\log_3{N}} \rceil} = 24$ + +We can compare them using base 3 instead of base 2 using a similar idea as the previous solution. + +#### 3.4 Solution with $x = 3 \times {\lceil {\log_3{N}} \rceil} - 2 = 22$ + +We can prune two states when processing the last digit (in base 3 representation). If the last digit is 0 or 2, then we can be sure whether $A < B$ or $A > B$ without needing to check the last digit from the other bag since we know that $A \neq B$. + +#### 3.5 Solution with $x = 3 \times {\lceil {\log_3{(N + 1)}} \rceil} - 3 = 21$ + +In the previous solution, we use the pruning idea on the last digit only. If we use the ternary number system like the previous solution, we can visualize it as a perfect ternary tree where we divide range [0, $3^k − 1$] into 3 parts recursively. And, the pruning in the previous solution is only applied for some leaf nodes (last digits). + +Instead of pruning only the last digit, we can apply it to any possible range. Let's say we know that both $A$ and $B$ are currently inside the range [$L, R$], and we are checking bag $A$. If we know that $A = L$, then we immediately know that $A < B$. If we know that $A = R$, then we know $A > B$. It means that we can "prune away" the leftmost and the rightmost integer from the range. After that, we can divide the range +[$L + 1, R − 1$] into 3 parts and determine which sub-range $A$ belongs to. Then, the next prisoner can check bag $B$ and determine which sub-range it belongs to. If both bags still belong to the same sub-range, then we can continue the process with this smaller range. Otherwise, we can already determine which bag has fewer coins. We continue this process until we can determine the answer. + +When we divide a range into 3 sub-ranges, we need 3 additional states. If the base range is a range with 2 integers, then $3 \times K$ states can cover $3 × (3 × (. . . 3 × (2) + 2 . . .) + 2) + 2 = 3^{K+1} − 1$ numbers. So, we need approximately $3 \times K = 3 \times ({\lceil {\log_3{(N + 1)}} \rceil} - 1) = 21$ states for $N = 5000$. + +#### 3.6 Solution with $x = 20$ + +We can optimize the previous solution. Dividing a range by 3 is not always optimal. We can use dynamic programming to determine what is the best divider for each level such that it can cover the largest range. + +$$ +dp[x] = \max_{1 \leq i \leq x} {(i \times dp[x - i] + 2)} +$$ + +where $dp[x]$ denotes the maximum range that can be covered using $x$ states and $dp[0]$ = 2. For $x = 20$, we can cover until $N = 5588$. Such $x$ is the minimum such that $N ≥ 5000$. It can be achieved by dividing the range [1, 5588] by 3, 3, 3, 3, 3, 2, 2, and 1 for each level recursively using the same process as the previous solution.