Hacker rank Similar Pairs - algorithm

I am trying to solve hacker rank similar pairs https://www.hackerrank.com/contests/101hack/challenges/similarpair problem. I cant figure out why its failing for large test cases. I am using segment trees to solve this problem in nlogn time. You can find my code below.
#include<iostream>
#include<vector>
using namespace std;
vector<int> graph[110001];
int T, ST[100001*4] = {0}, N, deg[100001] = {0};
void update(int node, int b, int e, int idx, int val) {
if(b > node || e < node) return;
if(b == e) {
ST[idx] += val;
return;
}
update(node, b, (b + e)/2, 2 * idx, val);
update(node, (b + e)/2 + 1, e, 2 * idx + 1, val);
ST[idx] = ST[2 * idx] + ST[2 * idx + 1];
}
long Query(int l, int r, int b, int e, int idx) {
if( l > e || r < b) return 0;
if(l <= b && r >= e) return ST[idx];
return Query(l, r, b, (b + e)/2, 2 * idx) + Query(l, r, (b + e)/2 + 1, e, 2 * idx + 1);
}
long long SimilarPairs(int node) {
int l = max(1, node - T), r = min(N, node + T);
long res = 0;
res = Query(l, r, 1, N, 1);
update(node, 1, N, 1, 1);
for(int i = 0; i < graph[node].size(); i++) {
res += SimilarPairs(graph[node][i]);
}
update(node, 1, N, 1, -1);
return res;
}
int main() {
long x, y, root, result, start;
cin >> N >> T;
for(int i = 0; i < N - 1; i++) {
cin >> x >> y;
graph[x].push_back(y);
deg[y]++;
}
for(int i = 1; i <= N; i++) if(!deg[i]) root = i;
result = SimilarPairs(root);
cout << result << endl;
cin.get();
return 0;
}

I get what you were doing. The problem is that you were missing some long longs. long is the same as an int (on 32 bits), so you must use long long everywhere, since the result does not necessarily fit in a 32 bit int.
This gets AC:
#include<iostream>
#include<vector>
using namespace std;
vector<int> graph[110001];
int T, N, deg[100001] = {0};
long long ST[100001*4] = {0};
void update(int node, int b, int e, int idx, int val) {
if(b > node || e < node) return;
if(b == e) {
ST[idx] += val;
return;
}
int m = (b + e) >> 1;
int q = idx << 1;
update(node, b, m, q, val);
update(node, m + 1, e, q + 1, val);
ST[idx] = ST[q] + ST[q+1];
}
long long Query(int l, int r, int b, int e, int idx) {
if( l > e || r < b) return 0;
if(l <= b && r >= e) return ST[idx];
int m = (b + e) >> 1;
int q = idx << 1;
return Query(l, r, b, m, q) + Query(l, r, m + 1, e, q + 1);
}
long long SimilarPairs(int node) {
int l = max(1, node - T), r = min(N, node + T);
long long res = 0;
res = Query(l, r, 1, N, 1);
update(node, 1, N, 1, 1);
for(int i = 0; i < graph[node].size(); i++) {
res += SimilarPairs(graph[node][i]);
}
update(node, 1, N, 1, -1);
return res;
}
int main() {
long x, y, root, start;
cin >> N >> T;
for(int i = 0; i < N - 1; i++) {
cin >> x >> y;
graph[x].push_back(y);
deg[y]++;
}
for(int i = 1; i <= N; i++) if(!deg[i]) root = i;
long long result = SimilarPairs(root);
cout << result << endl;
cin.get();
return 0;
}

Related

Runtime error for large inputs for sorting ( quicksort)

This is a very simple program where the user inputs (x,y) coordinates and distance 'd' and the program has to find out the number of unrepeated coordinates from (x,y) to (x+d,y).
For eg: if input for one test case is: 4,9,2 then the unrepeated coordinates are (4,9),(5,9) and (6,9)(x=4,y=9,d=2). I have used a sorting algorithm as mentioned in the question (to keep track of multiple occurrences) however the program shows runtime error for test cases beyond 30. Is there any mistake in the code or is it an issue with my compiler?
For a detailed explanation of question: https://www.hackerearth.com/practice/algorithms/sorting/merge-sort/practice-problems/algorithm/missing-soldiers-december-easy-easy/
#include <stdio.h>
#include <stdlib.h>
int partition(int *arr, int p, int r) {
int x;
x = arr[r];
int tmp;
int i = p - 1;
for (int j = p; j <= r - 1; ++j) {
if (arr[j] <= x) {
i = i + 1;
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
tmp = arr[i + 1];
arr[i + 1] = arr[r];
arr[r] = tmp;
return (i + 1);
}
void quicksort(int *arr, int p, int r) {
int q;
if (p < r) {
q = partition(arr, p, r);
quicksort(arr, p, q - 1);
quicksort(arr, q + 1, r);
}
}
int count(int A[],int ct) {
int cnt = 0;
for (int i = 0; i < ct; ++i) {
if (A[i] != A[i + 1]) {
cnt++;
}
}
return cnt;
}
int main() {
int t;
scanf("%d", &t);
long int tmp, y, d;
int ct = 0;
int i = 0;
int x[1000];
int j = 0;
for (int l = 0; l < t; ++l) {
scanf("%d%d%d", &tmp, &y, &d);
ct = ct + d + 1; //this counts the total no of coordinates for each (x,y,d)
for (int i = 0; i <= d; ++i) {
x[j] = tmp + i; //storing all possible the x and x+d coordinates
j++;
}
}
int cnt;
int p = ct - 1;
quicksort(x, 0, p); //quicksort sorting
for (int l = 0; l < ct; ++l) {
printf("%d ", x[l]); //prints sorted array not necessary to question
}
cnt = count(x, ct); //counts the number of non-repeated vertices
printf("%d\n", cnt);
}
The problem was the bounds of the array int x[1000] is not enough for the data given below.

Please spot the error in this merge sort code

I have provided the code for sorting an array using the merge sort algorithm, I'm unable to find the error, this code is not giving the correctly sorted array as it's output. The function mergesort is called recursively to divide the array till its size is reduced to 1. Then multiple arrays are merged using the merge function.
#include <bits/stdc++.h>
using namespace std;
void merge(int a[], int m, int l, int h) {
int n1 = m - l + 1, n2 = h - m;
int t1[n1], t2[n2];
for (int i = 0; i < n1; i++) {
t1[i] = a[i + l];
}
for (int i = 0; i < n2; i++) {
t2[i] = a[i + m + 1];
}
int k = 0, p = 0, r = 0;
while (k < n1 && p < n2) {
if (t1[k] <= t2[p]) {
a[r] = t1[k];
k++;
r++;
} else {
a[r] = t2[p];
p++;
r++;
}
}
while (k < n1) {
a[r] = t1[k];
k++;
r++;
}
while (p < n2) {
a[r] = t2[p];
p++;
r++;
}
}
void mergesort(int a[], int l, int h) {
if (l < h) {
int m = l + (h - l) / 2;
mergesort(a, l, m);
mergesort(a, m + 1, h);
merge(a, m, l, h);
}
}
int main() {
int a[5] = { 1, 2, 3, 4, 5 };
mergesort(a, 0, 4);
for (int i = 0; i < 5; i++) {
cout << a[i] << " ";
}
return 0;
}
The bug in the merge function is r should be initialized to l, not 0. You are not merging the slices into the original position.
Also note that the last loop while (p < n2) in this function is redundant: the remaining elements in the right slice are already in the proper place in the original array.
Here is a modified version:
void merge(int a[], int m, int l, int h) {
int n1 = m - l + 1, n2 = h - m;
int t1[n1], t2[n2];
for (int i = 0; i < n1; i++) {
t1[i] = a[i + l];
}
for (int i = 0; i < n2; i++) {
t2[i] = a[i + m + 1];
}
int k = 0, p = 0, r = l;
while (k < n1 && p < n2) {
if (t1[k] <= t2[p]) {
a[r] = t1[k];
k++;
r++;
} else {
a[r] = t2[p];
p++;
r++;
}
}
while (k < n1) {
a[r] = t1[k];
k++;
r++;
}
}
To further simplify the code, here are some more remarks:
it is less confusing to make use the convention that h be the first index beyond the end of the slice. This way the initial call uses the array length and mergesort can compute the slice length as h - l.
variable name l looks confusingly close to number 1.
the arguments to merge are usually in the order l, m, h, and m is the index of the start of the right slice.
the right slice does not need saving.
using variable length arrays with automatic storage t1[n2] may cause a stack overflow for large arrays.
Here is a modified version:
#include <bits/stdc++.h>
using namespace std;
void merge(int a[], int lo, int m, int hi) {
int i, j, k;
int n1 = m - lo;
int t1[n1];
for (i = 0; i < n1; i++) {
t1[i] = a[lo + i];
}
i = 0;
j = m;
k = lo;
while (i < n1 && j < hi) {
if (t1[i] <= a[j]) {
a[k++] = t1[i++];
} else {
a[k++] = a[j++];
}
}
while (i < n1) {
a[k++] = t1[i++];
}
}
void mergesort(int a[], int lo, int hi) {
if (hi - lo >= 2) {
int m = lo + (hi - lo) / 2;
mergesort(a, lo, m);
mergesort(a, m, hi);
merge(a, lo, m, hi);
}
}
int main() {
int a[5] = { 1, 5, 2, 4, 3 };
mergesort(a, 0, 5);
for (int i = 0; i < 5; i++) {
cout << a[i] << " ";
}
cout << "\n";
return 0;
}

Merge sort: time limit exceed

Why I am getting time limit exceeded error in sorting array using merge sort algorithm? What is wrong with my code? I have taken an input of 9 elements.
Input: 4 2 1 8 5 9 6 7 0
Output: Time limit exceeded
#include <bits/stdc++.h>
using namespace std;
int a[100];
void merge(int a[], int l, int r, int m) {
int t[r - l + 1];
int i = l, j = m + 1, k = 0;
while (i <= m && j <= r) {
if (a[i] < a[j])
t[k++] = a[i++];
else
t[k++] = a[j++];
}
while (i <= m)
t[k++] = a[i++];
while (j <= r)
t[k++] = a[j++];
for (int i = l; i <= r; i++)
a[i] = t[i - l];
}
void msort(int a[], int l, int r) {
if (l > r)
return;
int m = (r + l) / 2;
msort(a, l, m);
msort(a, m + 1, r);
merge(a, l, r, m);
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
msort(a, 0, n - 1);
for (int i = 0; i < n; i++)
cout << a[i] << " ";
cout << endl;
return 0;
}
There are some problems in your code:
The test for termination in msort() is incorrect: you should stop when the slice has a single element or less. You currently loop forever on slices of 1 element.
if (l >= r) return;
You should test in main() if the number n of elements read from the user is no greater than 100, the size of the global array a into which you read the elements to be sorted. You should instead use a local array with the proper size or allocate the array from the heap. The temporary array t in merge() might also be too large for automatic allocation. It is more efficient to allocate temporary space once and pass it recursively.
Note also that it is idiomatic in C and C++ to specify array slices with the index of the first element and the index of the element after the last one. This simplifies the code and allows for empty arrays and avoid special cases for unsigned index types.
Here is a modified version with this approach:
#include <bits/stdc++.h>
using namespace std;
void merge(int a[], int l, int r, int m, int t[]) {
int i = l, j = m, k = 0;
while (i < m && j < r) {
if (a[i] < a[j])
t[k++] = a[i++];
else
t[k++] = a[j++];
}
while (i < m)
t[k++] = a[i++];
while (j < r)
t[k++] = a[j++];
for (int i = l; i < r; i++)
a[i] = t[i - l];
}
void msort(int a[], int l, int r, int t[]) {
if (r - l > 1) {
int m = l + (r - l) / 2;
msort(a, l, m, t);
msort(a, m, r, t);
merge(a, l, r, m, t);
}
}
void msort(int a[], int n) {
if (n > 1) {
int *t = new int[n];
msort(a, 0, n, t);
delete[] t;
}
}
int main() {
int n;
cin >> n;
if (n <= 0)
return 1;
int *a = new int[n];
for (int i = 0; i < n; i++)
cin >> a[i];
msort(a, n);
for (int i = 0; i < n; i++)
cout << a[i] << " ";
cout << endl;
delete[] a;
return 0;
}

Mapping a large number to a unique number using MOD operation

Let the Roots of a first degree polynomial( Q(x) ) be x0 = -b/a. Since the range of the variable a and b is large, x0 can be large as well for it to be stored in a variable(x0).
so, it is converted to some unique number using some operation with mod
int x0 = mul(mod - b, rev(a));
problem link: hackerank problem
Can someone please explain how this line of code works and the math behind this operation?
the whole code-
#include <bits/stdc++.h>
using namespace std;
#define forn(i,n) for (int i = 0; i < int(n); ++i)
typedef long long ll;
const int inf = int(1e9) + int(1e5);
const ll infl = ll(2e18) + ll(1e10);
const int mod = 1e9 + 7;
int udd(int &a, int b) {
a += b;
if (a >= mod)
a -= mod;
return a;
}
int add(int a, int b) {
return udd(a, b);
}
int mul(ll a, ll b) {
return a * b % mod;
}
//============didnt understand this step
int bin(int a, int d) {
int b = 1;
while (d) {
if (d & 1)
b = mul(b, a);
d >>= 1;
a = mul(a, a);
}
return b;
}
int rev(int a) {
assert(a != 0);
return bin(a, mod - 2);
}
const int maxn = 100100;
int px[maxn];
int c[maxn];
struct Fenwick {
int a[maxn];
int t[maxn];
void set(int pos, int val) {
int delta = add(val, mod - a[pos]);
a[pos] = val;
delta = mul(delta, px[pos]);
for (int i = pos; i < maxn; i |= i + 1) {
udd(t[i], delta);
}
}
int get(int r) {
int res = 0;
for (int i = r - 1; i >= 0; i = (i & (i + 1)) - 1)
udd(res, t[i]);
return res;
}
int get(int l, int r) {
return add(get(r), mod - get(l));
}
} fw;
int main() {
#ifdef LOCAL
assert(freopen("test.in", "r", stdin));
#endif
int n, a, b, q;
cin >> n >> a >> b >> q;
//========what does this line do?
int x0 = mul(mod - b, rev(a));
px[0] = 1;
for (int i = 1; i < n; ++i)
px[i] = mul(px[i - 1], x0);
forn (i, n) {
cin >> c[i];
fw.set(i, c[i]);
}
forn (i, q) {
int t, a, b;
cin >> t >> a >> b;
if (t == 1) {
fw.set(a, b);
} else {
++b;
int s = fw.get(a, b);
if (x0 == 0)
s = fw.a[a];
cout << (s == 0 ? "Yes" : "No") << '\n';
}
}
}
bin is the halving-and-squaring implementation for the (in this case modular) power function a^d % mod, so that the modular inverse in rev can be computed via the little theorem of Fermat.

(ACM) How to use segment tree to count how many elements in [a,b] is smaller than a given constant?

I am quite new to segment tree and would like to make myself busy by doing some more exercise on segment tree.
The problem's actually more ACM like and have following conditions:
There are n numbers and m operations, n,m<=10,000, each operation can be one of the following:
1. Update an interval by minus a number x, x can be different each time
2. Query an interval to find how many numbers in the interval is <= 0
Building the segment tree and updating here is obviously can be done in O(nlog n) / O(log n)
But I cannot figure out how to make a query in O(log n), can anyone give me some suggestions / hints?
Any suggestions would be helpful! Thanks!
TL;DR:
Given n numbers, and 2 type operations:
add x to all elements in [a,b], x can be different each time
Query number of elements in [a,b] is < C, C is given constant
How to make operation 1 & 2 both can be done in O(log n)?
Nice Problem:)
I think for a while but still can't work out this problem with segment tree, but I've tried using "Bucket Method" to solve this problem.
We can divide the initial n numbers into B buckets, sort the number in each buckets and maintain the total add val in each bucket. Then for each query:
"Add" update interval [a, b] with c
we only need to rebuild at most two buckets and add c to (b - a) / BUCKET_SIZE buckets
"Query" query interval [a, b] <= c
we only need to scan at most two buckets with each value one by one and quick go through (b-a) / BUCKET_SIZE buckets with binary search quickly
It should be run in O( N/BUCKET_SIZE * log(BUCKET_SIZE, 2)) for each query, which is smaller than bruteforce method( O(N)). Though it's bigger than O(logN), it may be sufficient in most cases.
Here are the test code:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <ctime>
#include <cassert>
using namespace std;
struct Query {
//A a b c add c in [a, b] of arr
//Q a b c Query number of i in [a, b] which arr[i] <= c
char ty;
int a, b, c;
Query(char _ty, int _a, int _b, int _c):ty(_ty), a(_a), b(_b), c(_c){}
};
int n, m;
vector<int> arr;
vector<Query> queries;
vector<int> bruteforce() {
vector<int> ret;
vector<int> numbers = arr;
for (int i = 0; i < m; i++) {
Query q = queries[i];
if (q.ty == 'A') {
for (int i = q.a; i <= q.b; i++) {
numbers[i] += q.c;
}
ret.push_back(-1);
} else {
int tmp = 0;
for(int i = q.a; i <= q.b; i++) {
tmp += numbers[i] <= q.c;
}
ret.push_back(tmp);
}
}
return ret;
}
struct Bucket {
vector<int> numbers;
vector<int> numbers_sorted;
int add;
Bucket() {
add = 0;
numbers_sorted.clear();
numbers.clear();
}
int query(int pos) {
return numbers[pos] + add;
}
void add_pos(int pos, int val) {
numbers[pos] += val;
}
void build() {
numbers_sorted = numbers;
sort(numbers_sorted.begin(), numbers_sorted.end());
}
};
vector<int> bucket_count(int bucket_size) {
vector<int> ret;
vector<Bucket> buckets;
buckets.resize(int(n / bucket_size) + 5);
for (int i = 0; i < n; i++) {
buckets[i / bucket_size].numbers.push_back(arr[i]);
}
for (int i = 0; i <= n / bucket_size; i++) {
buckets[i].build();
}
for (int i = 0; i < m; i++) {
Query q = queries[i];
char ty = q.ty;
int a, b, c;
a = q.a, b = q.b, c = q.c;
if (ty == 'A') {
set<int> affect_buckets;
while (a < b && a % bucket_size != 0) buckets[a/ bucket_size].add_pos(a % bucket_size, c), affect_buckets.insert(a/bucket_size), a++;
while (a < b && b % bucket_size != 0) buckets[b/ bucket_size].add_pos(b % bucket_size, c), affect_buckets.insert(b/bucket_size), b--;
while (a < b) {
buckets[a/bucket_size].add += c;
a += bucket_size;
}
buckets[a/bucket_size].add_pos(a % bucket_size, c), affect_buckets.insert(a / bucket_size);
for (set<int>::iterator it = affect_buckets.begin(); it != affect_buckets.end(); it++) {
int id = *it;
buckets[id].build();
}
ret.push_back(-1);
} else {
int tmp = 0;
while (a < b && a % bucket_size != 0) tmp += (buckets[a/ bucket_size].query(a % bucket_size) <=c), a++;
while (a < b && b % bucket_size != 0) tmp += (buckets[b/ bucket_size].query(b % bucket_size) <=c), b--;
while (a < b) {
int pos = a / bucket_size;
tmp += upper_bound(buckets[pos].numbers_sorted.begin(), buckets[pos].numbers_sorted.end(), c - buckets[pos].add) - buckets[pos].numbers_sorted.begin();
a += bucket_size;
}
tmp += (buckets[a / bucket_size].query(a % bucket_size) <= c);
ret.push_back(tmp);
}
}
return ret;
}
void process(int cas) {
clock_t begin_t=clock();
vector<int> bf_ans = bruteforce();
clock_t bf_end_t =clock();
double bf_sec = ((1.0 * bf_end_t - begin_t)) / CLOCKS_PER_SEC;
//bucket_size is important
int bucket_size = 200;
vector<int> ans = bucket_count(bucket_size);
clock_t bucket_end_t =clock();
double bucket_sec = ((1.0 * bucket_end_t - bf_end_t)) / CLOCKS_PER_SEC;
bool correct = true;
for (int i = 0; i < ans.size(); i++) {
if (ans[i] != bf_ans[i]) {
cout << "query " << i + 1 << " bf = " << bf_ans[i] << " bucket = " << ans[i] << " bucket size = " << bucket_size << " " << n << " " << m << endl;
correct = false;
}
}
printf("Case #%d:%s bf_sec = %.9lf, bucket_sec = %.9lf\n", cas, correct ? "YES":"NO", bf_sec, bucket_sec);
}
void read() {
cin >> n >> m;
arr.clear();
for (int i = 0; i < n; i++) {
int val;
cin >> val;
arr.push_back(val);
}
queries.clear();
for (int i = 0; i < m; i++) {
char ty;
int a, b, c;
// a, b, c in [0, n - 1], a <= b
cin >> ty >> a >> b >> c;
queries.push_back(Query(ty, a, b, c));
}
}
void run(int cas) {
read();
process(cas);
}
int main() {
freopen("bucket.in", "r", stdin);
//freopen("bucket.out", "w", stdout);
int T;
scanf("%d", &T);
for (int cas = 1; cas <= T; cas++) {
run(cas);
}
return 0;
}
and here are the data gen code:
#coding=utf8
import random
import math
def gen_buckets(f):
t = random.randint(10, 20)
print >> f, t
nlimit = 100000
mlimit = 10000
limit = 100000
for i in xrange(t):
n = random.randint(1, nlimit)
m = random.randint(1, mlimit)
print >> f, n, m
for i in xrange(n):
val = random.randint(1, limit)
print >> f, val ,
print >> f
for i in xrange(m):
ty = random.randint(1, 2)
a = random.randint(0, n - 1)
b = random.randint(a, n - 1)
#a = 0
#b = n - 1
c = random.randint(-limit, limit)
print >> f, 'A' if ty == 1 else 'Q', a, b, c
f = open("bucket.in", "w")
gen_buckets(f)
Try applying a Binary Index Trees (BIT) instead of a segmented tree. Here's the link to the tutorial

Resources