#88 D
problem link http://codeforces.com/contest/117/problem/D
The transformation is a recursive process.
For example:
L0: (1 2 3 4 5 6 7 8 9 10) D = 1 <==> 2^0
L1: (1 3 5 7 9) (2 4 6 8 10) D = 2 <==> 2^1
L2: (1 5 9) (3 7) (2 6 10) (4 8) D = 4 <==> 2^2
L3: (1 9) (5) (3) (7) (2 10) (6) (4) (8) D = 8 <==> 2^3
L4: (1) (9) (5) (3) (7) (2) (10) (6) (4) (8)
Because the array size is very big (10^18), so storing the whole array in memory is imposible, but thanks to this precondition, the problem become interesting.
At first, I try hard to figure out what position will a number at position P in L1 be when the transformation finished (L4 in the example) .
I found the regulation below:
At Level i, let the number at position p(i) in its group (see the example for group definition)
If p(i) is odd, then its position in the next level group will be p(i+1) = (p(i)+1)/2.
else, will be p(i+1) = p(i) / 2.
Further more, let the number at position P(i) in the whole level i, and the group size is G(i),
If p(i) ( not P ) is odd, then its position in the next whole level will be P(i+1) = P(i) - p(i) + ( p(i)+1 )/2
else, if G(i) is odd, then
P(i+1) = P(i) - p(i) + (G(i)+1)/2 + p(i)/2
else,
P(i+1) = P(i) - p(i) + G(i)/2 + p(i)/2
By this regulation, I can locate any number in the array in the final level, but soon I found that is useless for this problem, because if my process unit is every single number in the array, time limit will be exceeded.
But this regulation helps me to find other regulation.
let's consider the most simple situation: the query range is exactly the L0 group ( the whole array ), then I can ignore the pattern of the array in any higher level, because the only group in level 1 is a geometric series.
Giving the lower bound (u) and upper bound (v) in the query, and the first number as well as the tolerance(公差)in the group , I can locate the first number (L) and the final number (R) that satisfy the bound. (See the code at the end of the article)
Now developing this regulation to general situation.
Every query range is formed by many sub-ranges, and every group in every level is also a geometric series, tolerance is 2^D, where D is the level of that group. So what the algorithm needs to do is to match all subrange to the corresponding group, and do the calculation in that group.
Let's see this query Q(2, 8, 6 10) for the above array ( N = 10 )
L0: (1 2 3 4 5 6 7 8 9 10) D = 1 <==> 2^0
L1: (1 3 5 7 9) (2 4 6 8 10) D = 2 <==> 2^1
L2: (1 5 9) (3 7) (2 6 10) (4 8) D = 4 <==> 2^2
L3: (1 9) (5) (3) (7) (2 10) (6) (4) (8) D = 8 <==> 2^3
L4: (1) (9) (5) (3) (7) (2) (10) (6) (4) (8)
The bold groups is the target that match all subranges which contained by (2, 8)
The final level L4 has no tolerance, or its tolerance can be ignore.
The numbers with underline are the target numbers that sum the result.
The abstract alg is:
If the current group matches the query range, do calculation, and do no more deep group-search.
else, divide the current group from middle position M, and also divide the query range from M into two subrange lr and rr, and do the group-finding in these two subgroup with the subranges.
If the subrange is empty, then ignore the corresponding subgroup.
#include <stdio.h>
#include <math.h>
long long l, r, u, v;
long long N, ret, MOD, M, i, j, k, one=1;
void count(long long left, long long right, long long ql, long long qr, long long lv, long long D) {
if( left > right || ql > qr )
return ;
long long n, m, rv;
if( left == ql && right == qr ) {
n = right - left + 1;
rv = lv + ((n-1)<<D);
ql = (((u - lv + (one<<D) - 1)>>D)<<D) + lv;
qr = (((v - lv)>>D)<<D) + lv;
ql = (ql <= lv ? lv : ql );
qr = (qr >= rv ? rv : qr );
if( !(u <= ql && ql <= qr && qr <= v) )
return ;
n = ((qr - ql)>>D) + 1;
ret += (((ql + qr) % MOD ) * (((n-(n&1)) >> 1) % MOD));
ret += (((ql + qr) >> 1) * (n&1));
ret %= MOD;
return ;
}
m = (left + right) >> 1;
n = (m <= qr ? m : qr);
if( ql <= m )
count(left, m, ql, n, lv, D+1);
n = (m+1 >= ql ? m+1 : ql);
if( m < qr )
count(m+1, right, n, qr, lv+(one<<D), D+1);
return ;
}
int main() {
scanf("%I64d %I64d %I64d", &N, &M, &MOD);
for( i=0;i<M;i++ ) {
scanf("%I64d %I64d %I64d %I64d", &l, &r, &u, &v);
ret = 0;
count(1, N, l, r, 1, 0);
printf("%I64d\n", ret);
}
return 0;
}
In order to reduce the execution time, I replace division and multiplication with shift operation as posible as I can.
One of the difficulty in this problem is using modular, never do modular before division if the modular target expression includes the division.
Say, if target expression is (A / B % M), ((A % M) / B) % M is not always correct.