Search code examples
c++ctimeperformanceexecution

how to make the execution time less(ie. a faster code) for this problem


this question is from Codechef.com [if anyone is still solving this question dont look further into the post before trying yourself] and although it runs right, but i need to make it a lot faster.i am a beginner in c,c++.(i know stuff upto arrays,strings and pointers but not file handling etc).So is there a way to make this program run any faster without making it complex(its ok if its complex in algorithm). i will accept complex coding too if u mention which book u followed where it is all given :) . i currently am following Robert Lafore. Here is the program:-

There are N numbers a[0],a[1]..a[N - 1]. Initally all are 0. You have to perform two types of operations :

1) Increase the numbers between indices A and B by 1. This is represented by the command "0 A B"

2) Answer how many numbers between indices A and B are divisible by 3. This is represented by the command "1 A B".

Input :

The first line contains two integers, N and Q. Each of the next Q lines are either of the form "0 A B" or "1 A B" as mentioned above.

Output :

Output 1 line for each of the queries of the form "1 A B" containing the required answer for the corresponding query.

Sample Input :

4 7
1 0 3
0 1 2
0 1 3
1 0 0
0 0 3
1 3 3
1 0 3

Sample Output :

4
1
0
2

Constraints :

1 <= N <= 100000
1 <= Q <= 100000
0 <= A <= B <= N - 1

HERE IS MY SOLUTION:-

#include<stdio.h>

int main()
{
    unsigned int n; //amount of numbers taken
    scanf("%u",&n);

    unsigned int arr[n],q,a,b,count=0,j,i;
    short int c;
    scanf("%u",&q);//cases taken  
    for(i=0;i<n;++i)
    {
      arr[i]=0;
    }    


    for(i=0;i<q;++i)
    {
      scanf("%d%u%u",&c,&a,&b);//here a,b are A and B respectively and c is either 
                               //o or 1

      if(c==0)
      {
        for(j=a;j<=b;++j)
          arr[j]++;
      }


      if(c==1)
      {
        for(j=a;j<=b;++j)
        {
          if(arr[j]%3==0)
            count++;
        }
       printf("%u\n",count);
      }
      count=0;
    }  

}

Solution

  • 1) Increase the numbers between indices A and B by 1. This is represented by the command "0 A B"

    2) Answer how many numbers between indices A and B are divisible by 3. This is represented by the command "1 A B".

    Initially numbers are 0 and thus are divisible by 3. Increment by one make the number non-divisible. Next increment - number still non-divisible. Third increment makes the number again divisible.

    First optimization one can try is to not to let number grow above 2: if during increment number goes from 2 to 3, set it back to zero. Now search for the range become a simple comparison with 0. (That way array would contain instead of the number its modulo 3.)

    Second optimization is to use extents instead of plain array, e.g. something similar to the RLE: collapse into a range all adjacent numbers with the same divisibility. Instead of numbers, the array would contain structures like that:

    struct extent {
       int start; /* 0 .. N-1; extent should have at least one number */
       int end;   /* 0 .. N   */
       int n;     /* 0, 1, 2; we are only interested in the result of % */
    };
    

    Initially the array would contain single extent covering the all numbers {0, N, 0}. During increment step, a range might be split or joined with adjacent one. That representation would speed-up the counting of numbers as you would go over the array not one-by-one but in chunks. (It would still degrade to the linear search if all ranges are contain only one element.)


    Another approach could be to use instead of the array three sets with indeces. Set #0 would contain all the indeces of numbers whose modulo 3 is 0, set #1 - 1, set #2 - 2. Since during increment operation, we need to do a search, instead of std::set it is better to use e.g. std::bitset with every bit marking the index of the number belonging to the set.

    N.B. This way we do not keep the original numbers at all. We implicitly keep only the result of modulo 3.

    During increment, we need to find which set the index belongs to, e.g. set #n, and move the index to the next (mod 3) set: set the bit to zero in the set n, set the bit to 1 in the set n + 1 (mod 3). Counting numbers divisible by 3 now ends up being as simple as counting the non-zero bits in the set #0. That can be accomplished by creating a temp std::bitset as a mask with bits in range [A,B] set to one, masking with the temp set the set #0 and calling std::bitset::count() on the resulting bitset.