Divide and Conquer algorithm in C++ - c++

There is this one problem in some online judge that I have no clue on how to get accepted.
The problem goes like this first line contained two number
N (0 < N < 2^18)
M (0 < M < 2^20)
The second line contained N numbers
ai (0 < ai < 2^40)
The question is how many X are there that satisfied:
M = floor(X/a1) + floor(X/a2) + ... + floor(X/an)
My naive solution:
#include<bits/stdc++.h>
using namespace std;
long long n,m,i,j,haha,sum;
int main()
{
cin >> n >> m;
haha = 0;
long long ar[n+5];
for(i = 0; i < n; i++) cin >> ar[i];
sort(ar,ar+n);
for(i = ar[0]+1; i < m*ar[0]; i++){
sum = 0;
for (j = 0; j < n; j++) sum += i/ar[j];
if (sum == m) haha += 1;
else if (sum >= m) break;
}
cout << haha << endl;
}
Update1:
My binary search solution (still didn't pass the time limit):
#include<bits/stdc++.h>
using namespace std;
long long n,m,i,l,r,mid,ans,tmp,cnt,haha;
long long ar[2621440];
long long func(long long x){
haha = 0;
for (i = 0; i < n; i++) haha += x/ar[i];
return haha;
}
int main()
{
cin >> n >> m;
for(i = 0; i < n; i++) cin >> ar[i];
sort(ar,ar+n);
l = ar[0];
r = ar[0]*m;
mid = (l+r)/2;
tmp = func(mid);
while (tmp != m){
mid = (l+r)/2;
tmp = func(mid);
if (l == r) break;
if (tmp < m) l = mid+1;
else if (tmp > m) r = mid-1;
else break;
}
ans = 0;
if (tmp == m) ans += 1;
cnt = mid;
while (func(cnt-1) == m){
ans += 1;
cnt -= 1;
}
cnt = mid;
while (func(cnt+1) == m){
ans += 1;
cnt += 1;
}
cout << ans << endl;
}

Update
Going with the binary search approach, here is my new code:
// compute X/ai sum
long long summarize(long long ar[], long long n, long long X)
{
long long sum = 0;
for (long long i = 0; i < n; i++)
{
sum += X/ar[i];
}
return sum;
}
bool get_range(long long ar[], int n, int m, pair<long long, long long>& range)
{
long long sum = 0;
long long x;
// reduce range
while (range.first < range.second)
{
x = (range.first + range.second) / 2;
sum = summarize(ar, n, x);
if (sum < m)
{
range.first = x + 1;
}
else if (sum > m)
{
range.second = x;
}
else if (x == range.first)
{
return true; // single element
}
else
{
break;
}
}
if (sum != m)
{
return false;
}
// check surroundings for lower / upper bound.
sum = summarize(ar, n, range.first);
if (sum != m)
{
auto r1 = make_pair(range.first + 1, x);
if (get_range(ar, n, m, r1))
{
range.first = r1.first;
}
else
{
range.first = x;
}
}
sum = summarize(ar, n, range.second - 1);
if (sum != m)
{
auto r2 = make_pair(x + 1, range.second - 1);
if (get_range(ar, n, m, r2))
{
range.second = r2.second;
}
else
{
range.second = x + 1;
}
}
return true;
}
int main()
{
int n, m;
cin >> n >> m;
long long *ar = new long long[n];
long long ar_min = LLONG_MAX;
for(long long i = 0; i < n; i++)
{
cin >> ar[i];
ar_min = min(ar[i], ar_min);
}
// initial range of possible X values
auto range = make_pair(m / (ar_min * n), m * ar_min);
if (get_range(ar, n, m, range))
{
cout << (range.second - range.first) << endl;
}
else
{
cout << 0 << endl;
}
}
Core functionality is the get_range function, which takes a possible range ([range.first, range.second), so second is not part of the range) and reduces the range so all elements in range satisfy the condition. It is first iteratively adjusting range bounds until the middle of the range is part of the result or until it's clear that there is no result in range. Then, if there is any result, it is recursively checking the sub-ranges below and above the found result in order to retrieve the bounds of the whole result range.
Version 1
You are only dealing with positive numbers greater than zero.
M = floor(X/a1) + floor(X/a2) + ... + floor(X/an)
For every sub-term floor(X/a1), there is floor(X1/ai) <= floor(X2/ai) if X1 < X2. So the only possible X values resulting in M are those, where floor(X1/ai) == floor(X2/ai) for all i (or all ai).
For each ai this is exactly the Range of X1=k*ai until X2=k*ai+(ai-1) for some k.
This means, if any solution exists, the range of X values will be between k*min(ai) and (k+1)*min(ai) for some 0 < k <= m.
So it might be worth to first get the range of possible results and then check the individual values only within the range.
Resulting algorithm:
// compute X/ai sum
long long summarize(long long ar[], long long n, long long X)
{
long long sum = 0;
for (long long i = 0; i < n; i++)
{
sum += X/ar[i];
}
return sum;
}
int main()
{
int n, m;
cin >> n >> m;
long long *ar = new long long[n];
long long ar_min = LLONG_MAX;
for(long long i = 0; i < n; i++)
{
cin >> ar[i];
ar_min = min(ar[i], ar_min);
}
// lowest possible k
long long k = m / (ar_min * n);
// get the value k for a possible range of X values
for (; k <= m; k++)
{
auto x = ar_min * (k + 1);
long long sum = summarize(ar, n, x);
if (sum > m)
{
break;
}
}
long long X_min = k * ar_min, X_max = (k + 1) * ar_min;
long long result = 0;
// count possible X values
for (long long x = X_min; x < X_max; x++)
{
long long sum = summarize(ar, n, x);
if (sum == m)
{
++result;
}
else if (sum > m)
{
break;
}
}
cout << result << endl;
}
It got a bit more complicated than I first expected. I hope it's still some sort of improvement.

I believe that the expected solution for this is binary search.
Define f(x) = sum_i f(x/a_i). Without loss of generality, assume that a_i are given in inceasing order.
Clearly,
f(0) = 0 < M
f(M*a_1) ≥ M
f(x) ≥ f(y) if x≥y
Thus you can do binary search to find the lowest value of x such that f(x) = M, with start = 0 and end = M*a_1 as the initial limits for the binary search.
To find the upper limit for x, do another binary search or just loop through all values in the array to find the smallest y such that floor(y/ai) > floor(x/ai) for some i.

Got accepted (finally) using two binary search (each for lower bound, and upper bound) with this code:
#include<bits/stdc++.h>
using namespace std;
long long n,m,i,l,r,mid1,mid2,ans,tmp,cnt,haha,k;
long long ar[26214400];
long long func(long long x){
haha = 0;
for (k = 0; k < n; k++) haha += x/ar[k];
return haha;
}
int main()
{
cin >> n >> m;
for(i = 0; i < n; i++) cin >> ar[i];
sort(ar,ar+n);
l = ar[0];
r = ar[0]*m;
mid1 = (l+r)/2;
tmp = func(mid1);
while (l < r){
mid1 = (l+r)/2;
tmp = func(mid1);
if (tmp < m) l = mid1+1;
else if (tmp > m) r = mid1-1;
else r = mid1-1;
}
mid1 = l; //lower bound
l = ar[0];
r = ar[0]*m;
mid2 = (l+r)/2;
tmp = func(mid2);
while (l < r){
mid2 = (l+r)/2;
tmp = func(mid2);
if (tmp < m) l = mid2+1;
else if (tmp > m) r = mid2-1;
else l = mid2+1;
}
mid2 = r; //upper bound
while (mid1 <= mid2 and func(mid1) != m) mid1 += 1;
while (mid2 >= mid1 and func(mid2) != m) mid2 -= 1;
ans = mid2-mid1+1;
cout << ans << endl;
}

Related

print condition while integer overflow not working

#include <iostream>
using namespace std;
int getFectorial(int n)
{
int ans = 1;
for (int i = n; i >= 1; i--)
{
ans = ans * i;
}
return ans;
}
int printNcr(int n, int r)
{
if (getFectorial(n) > INT_MAX)
{
return 0;
}
return (getFectorial(n)) / ((getFectorial(r)) * (getFectorial(n - r)));
}
int main()
{
int n = 14;
for (int row = 0; row < n; row++)
{
for (int col = 0; col < row + 1; col++)
{
cout << printNcr(row, col) << " ";
}
cout << endl;
}
return 0;
}
When I give value of n more than 13th I want integer overflow condition should be working that given in printNcr() function, but it's not working and all line after 13th are printing wrong values instead of returning false.
How to make given INT_MAX condition work?
int oveflow cannot be reliably detected after it happens.
One way to detect upcoming int overflow in factorial:
int getFactorial(int n) {
if (n <= 0) {
return 1; // and maybe other code when n < 0
}
int limit = INT_MAX/n;
int ans = 1;
for (int i = 2; i <= n; i++) {
if (ans >= limit) {
return INT_MAX; // Or some other code
}
ans = ans * i;
}
return ans;
}
Another way is at startup, perform a one-time calculation for maximum n. With common 32-bit int, that limit is 12.
int getFactorial(int n) {
if (n > getFactorial_pre_calculated_limit) {
return INT_MAX;
}
...
You can detect overflow by watching for negative value
int getFectorial(int n)
{
int ans = 1;
for (int i = n; i >= 1; i--)
{
ans = ans * i;
if (ans < 0) <<<<======
return -1;
}
return ans;
}
then
int printNcr(int n, int r)
{
if (getFectorial(n) < 0)
{
return 0;
}
return (getFectorial(n)) / ((getFectorial(r)) * (getFectorial(n - r)));
}
Please note though that strictly speaking this is undefined behavior. It would be better to simply fail if you know the result is going to be too big (Ie n > 13)
Or better do it like this
int getFectorial(int n)
{
long long ans = 1; <<<====
for (int i = n; i >= 1; i--)
{
ans = ans * i;
if (ans >INT_MAX) <<<<======
return -1;
}
return (int)ans;
}
or you could throw std::overflow_error
BTW the word is factorial not fectorial

prime seive algorithm giving a runtime error

I am trying to implement the Sieve of Eratosthenes algorithm but it giving a runtime error.
didn't get any output though. after providing the input,
#include<iostream>
using namespace std;
//Sieve Approach - Generate an array containing prime Numbers
void prime_sieve(int *p) {
//first mark all odd number's prime
for (int i = 3; i <= 10000; i += 2) {
p[i] = 1;
}
// Sieve
for (long long int i = 3; i <= 10000; i += 2) {
//if the current number is not marked (it is prime)
if (p[i] == 1) {
//mark all the multiples of i as not prime
for (long long int j = i * i; j <= 10000; j = j + i ) {
p[j] = 0;
}
}
}
//special case
p[2] = 1;
p[1] = p[0] = 0;
}
int main() {
int n;
cin >> n;
int p[10000] = {0};
prime_sieve(p);
//lets print primes upto range n
for (int i = 0; i <= n; i++) {
if (p[i] == 1) {
cout << i << " ";
}
}
return 0;
}
compiler didn't throwing any error also it is not providing the output also
program freezes for some seconds and then terminates
As mentioned in the comments, you are going out of bound.
There is also some confusion about the meaning of p[].
In addition, you are not using the value of n in the function, which leads to unnecessary calculations.
Here is a tested programme (up to n = 10000):
#include <iostream>
#include <vector>
#include <cmath>
//Sieve Approach - Generate an array containing prime Numbers less than n
void prime_sieve(std::vector<int> &p, long long int n) {
//first mark all odd number's prime
for (long long int i = 4; i <= n; i += 2) {
p[i] = 0;
}
// Sieve
for (long long int i = 3; i <= sqrt(n); i += 2) {
//if the current number is not marked (it is prime)
if (p[i] == 1) {
//mark all the multiples of i as not prime
for (long long int j = i * i; j <= n; j = j + i ) {
p[j] = 0;
}
}
}
//special cases
p[1] = p[0] = 0;
}
int main() {
long long int n;
std::cout << "Enter n: ";
std::cin >> n;
std::vector<int> p (n+1, 1);
prime_sieve(p, n);
//lets print primes upto range n
for (long long int i = 0; i <= n; i++) {
if (p[i] == 1) {
std::cout << i << " ";
}
}
return 0;
}

how to Calculate this series

#include <iostream>
#include <math.h>
using namespace std;
int fact(int number)
{
unsigned long long int p = 1;
if (number == 0) {
return p;
}
for (int i = 1; number >= i; i++) {
p = p * i;
}
return p;
}
int main()
{
long long int a, x, sum = 0, result;
int n ;
cin >> a;
cin >> x;
cin >> n;
for (int k = 0; n >= k; k++) {
result = fact(n) / (fact(k) * fact(n - k));
sum = sum + (result * pow(x, k) * pow(a, n - k));
}
cout << sum;
return 0;
}
I want to calculate this series
So I considered the long long int sum, but the sum number sometimes gets too big. What can I do to save the sum number without using library?
First of all I would suggest to use binomial theorem -- what you are computing is just pow(x+a, n)
If you want to do this through series, do not compute the binomial coefficient using factorials but something like this
int bin_coeff(int n, int k){
int lim = k > n/2 ? k : n - k;
int sum = 1;
for (int i = n; i > lim; i--){
sum *= i;
}
for (int i = 2; i < (n - lim + 1); i++){
sum /= i;
}
return sum;
}

Counting inversions in C++ via mergesort

I have a text file that contains 100,000 numbers from 1 ~ 100,000 in an unsorted manner (no duplicates).
The name of the file is "IntegerArray.txt"
My task is to count the number of inversions in the text file. An inversion is a pair of elements a, b where a comes before b, but a > b. Thus "1 2 3 6 4 5" contains two inversion (6 > 4 and 6 > 5).
I implemented it with the merge sorting method; the sorting works, but the counting inversions part kept giving out wrong answers, and I cannot find why.
The following is my code:
long long mergeAndCount(vector<int>& vec, int p, int q, int r) {
long long count = 0;
vector<int> L, R;
for (int i = p; i <= q; ++i) L.push_back(vec[i]);
for (int j = q + 1; j <= r; ++j) R.push_back(vec[j]);
L.push_back(numeric_limits<int>::max()); //sentinel element
R.push_back(numeric_limits<int>::max());
int i = 0, j = 0;
for (int k = p; k <= r; ++k) {
if (L[i] <= R[j]) {
vec[k] = L[i];
++i;
} else {
vec[k] = R[j];
++j;
if (L[i] != L.back() && R[j] != R.back())
// Problem SOLVED: change this line to count += q - p + 1 - i
count += q - i + 1;
}
}
return count;
}
long long inversion(vector<int>& vec, int p, int r) {
long long count = 0;
if (p < r) {
int q = (p + r) / 2;
count = inversion(vec, p, q);
count += inversion(vec, q + 1, r);
count += mergeAndCount(vec, p, q, r);
}
return count;
}
int main() {
ifstream infile("IntegerArray.txt");
int a;
vector<int> vec;
while (infile >> a)
vec.push_back(a);
cout << inversion(vec, 0, vec.size()-1);
return 0;
}
The result from the above code is 32620796130, which is incorrect.
The answer by brute force with the following code is 2407905288, which is correct.
long long inversion(vector<int>& vec, int p, int r) {
long long count = 0;
for (int i = 0; i < vec.size(); ++i)
for (int j = i + 1; j < vec.size(); ++j)
if (vec[i] > vec[j])
++count;
return count;
}
Can someone help me out solving this?

Count numbers in range divisible by K with a sum of digits in range

I have such a task:
Let's call a number "lucky" if it is divisible by K and sum of its digits lays in [P, Q]. Count the quantity of "lucky" numbers in [A, B].
1 ≤ A, B < 10^11,
1 ≤ P, Q ≤ 99,
1 ≤ K ≤ 10^11.
Time for each test - 5 seconds
Here is my code
#include <iostream>
#include <fstream>
using namespace std;
int sum;
long long k;
int number[10];
int digits[10];
int p;
int q;
long long a;
long long b;
int delta;
bool flag = false;
long long memory[100000][100][10][2];
long long pow (int a, int b)
{
long long res = 1;
for(int i = 0; i < b; i++)
res *= a;
return res;
}
int sumOfDigits(long long number)
{
int sum = 0;
while(number != 0)
{
sum += number % 10;
number /= 10;
}
return sum;
}
long long toLong(int digits[], int n)
{
long long sum = 0;
for(int i = 0; i < n; i++)
{
sum *= 10;
sum += digits[i];
}
return sum;
}
long long function(long long remainder// the remainder of division by k,
int currSum // current sum of digits,
int n //number of digit to be added,
bool allDigitsAllowed //says if all digits can be added to number)
{
if(n == 10)
{
int counter = 0;
for(int i = p; i <= q; i++)
{
long long res = i - currSum + remainder;
if(res%k == 0 && (allDigitsAllowed || i-currSum <= number[9]))
{
counter++;
if(!allDigitsAllowed && number[9] == i-currSum)
flag = true;
}
}
return counter;
}
long long res = 0;
int temp = 0;
while(currSum + temp <= q)
{
long long tempRemainder = (temp*pow(10,10-n)+remainder)%k;
if(temp == number[n-1] && !allDigitsAllowed)
{
if(tempRemainder < 100000)
{
if(memory[tempRemainder][currSum+temp][n+1][false] == -1)
memory[tempRemainder][currSum+temp][n+1][false] = function(tempRemainder, currSum + temp, n+1,false);
res += memory[tempRemainder][currSum+temp][n+1][false];
}
else
res += function(tempRemainder, currSum + temp, n+1,false);
//res += function(tempRemainder, currSum + temp, n+1,false);
break;
}
else
{
if(tempRemainder < 100000)
{
if(memory[tempRemainder][currSum+temp][n+1][true] == -1)
memory[tempRemainder][currSum+temp][n+1][true] = function(tempRemainder, currSum + temp, n+1,true);
res += memory[tempRemainder][currSum+temp][n+1][true];
}
else
res += function(tempRemainder, currSum + temp, n+1,true);
}
temp++;
}
return res;
}
long long F (long long a)
{
flag = false;
memset(&number, 0, sizeof(number));
sum = 0;
int i = 9;
while(a != 0)
{
number[i] = a%10;
a /= 10;
i--;
}
for(int j = 0; j < 10; j++)
swap(number[j],number[9-j]);
return function(0,0,1,false);
}
int main()
{
ifstream in("lucky.in");
ofstream out("lucky.out");
in >> k >> p >> q >> a >> b;
sum = p;
memset(&memory, -1, sizeof(memory));
long long result = - F(a) + flag;
memset(&memory, -1, sizeof(memory));
out<< result + F(b) << endl;
return 0;
}
In this solution I try to make a number with a determined sum of digits, adding digits one by one.
The problem is that I can't memorize all the the remainders of the division by k.
So how can I deal with this problem?
I would use something like:
unsigned int compute_number_sum(uint64_t n)
{
unsigned int res = 0;
while (n != 0) {
const uint64_t nd = n / 10;
res += static_cast<unsigned int>(n - 10 * nd); // equivalent to res += n % 10
n = nd;
}
return res;
}
uint64_t count_lucky_number(uint64_t A, uint64_t B, uint64_t K,
unsigned int P, unsigned int Q)
{
uint64_t res = 0;
const uint64_t min_i = (A + K - 1) / K * K;
for (uint64_t i = min_i; i <= B; i += K) {
const unsigned int numberSum = compute_number_sum(i);
res += P <= numberSum && numberSum <= Q;
}
return res;
}