0

the problem statement is the following:

Xorq has invented an encryption algorithm which uses bitwise XOR operations extensively. This encryption algorithm uses a sequence of non-negative integers x1, x2, … xn as key. To implement this algorithm efficiently, Xorq needs to find maximum value for (a xor xj) for given integers a,p and q such that p<=j<=q. Help Xorq to implement this function.

Input

First line of input contains a single integer T (1<=T<=6). T test cases follow.

First line of each test case contains two integers N and Q separated by a single space (1<= N<=100,000; 1<=Q<= 50,000). Next line contains N integers x1, x2, … xn separated by a single space (0<=xi< 2^15). Each of next Q lines describe a query which consists of three integers ai,pi and qi (0<=ai< 2^15, 1<=pi<=qi<= N).

Output

For each query, print the maximum value for (ai xor xj) such that pi<=j<=qi in a single line.

int xArray[100000];
cin >>t; 
for(int j =0;j<t;j++)   
{        
    cin>> n >>q;
    
    //int* xArray = (int*)malloc(n*sizeof(int));
    int i,a,pi,qi;        
    
    for(i=0;i<n;i++)
    {
        cin>>xArray[i];                         
    }
    
    for(i=0;i<q;i++)
    {
      cin>>a>>pi>>qi;
        int max =0;
      for(int it=pi-1;it<qi;it++)
      {
          int t =  xArray[it] ^ a;
          if(t>max)               
              max =t;
          
      }   
      cout<<max<<"\n" ;
    } 

No other assumptions may be made except for those stated in the text of the problem (numbers are not sorted). The code is functional but not fast enough; is reading from stdin really that slow or is there anything else I'm missing?

Community
  • 1
  • 1
Pandrei
  • 4,843
  • 3
  • 27
  • 44
  • Formatted input with `cin` and `operator>>` can be slow, but also can formatted output (`cout << ...`). You'll probably find that most of the time this function takes to run is just with input and output, rather than the bit manipulation. – the_mandrill Nov 28 '13 at 09:33
  • 1
    No, the problem is not reading from stdin. Yes, you are missing something. Namely, implementing a dumb, trivial, brute-force algorithm is *not* the point of this contest. You are supposed to come up with a faster algorithm. – n. m. could be an AI Nov 28 '13 at 09:39
  • 1
    @Barmar - neither; sharpening the programming skills; there are several sites which can help with this (including TopCoder.com); Please note I already solved the problem I just have difficulties optimizing it. – Pandrei Nov 28 '13 at 09:44
  • ok, I tend to agree with you; My approach is O(Q*N) right now - the problem I have when trying to find something better is the fact that I need the numbers between indexes p and q; and the numbers are not sorted. Sorting all the numbers would mean I loose the order and sorting Q times only the numbers of interest would actually take longer than what I'm doing now. I don't expect anyone to just write the solution for me - I'm looking for an idea to try out. – Pandrei Nov 28 '13 at 09:51
  • you are doing too much work in your innermost loop as it is obsolete. The task is only to find `max{a xor x_(p-1); a xor x_(q-1)}` – ogni42 Nov 28 '13 at 10:25
  • @ogni42: the X keys are not sorted and you cannot simply presort all the X because you'd lose the information about their indexes that have to be later compared against Pi-Qi which in turn are different for every single A. Assuming I've read everything correctly;) – quetzalcoatl Nov 28 '13 at 10:33
  • Pandrei: I've replaced my previous answer with an actual idea of not brute-forcing. I've not thought it over very thorougly, but it might be worth trying. – quetzalcoatl Nov 28 '13 at 10:36
  • I suggest renaming the question to something more related to the problem... bit manipulation is not key... perhaps "algo: given I[0..n], for varying a,p,q find max(a^I[i]) where 0<=p<=i<=q<=n". – Tony Delroy Nov 28 '13 at 11:42
  • This question is in Twitter programming challenge :D – Pham Trung Nov 28 '13 at 14:35
  • I don't know anything about that - what is twitter programming challenge (I personally found it on topcoder) – Pandrei Nov 28 '13 at 15:16

2 Answers2

0

XOR flips bits. The max result of XOR is 0b11111111.

To get the best result

  • if 'a' on ith place has 1 then you have to XOR it with key that has ith bit = 0
  • if 'a' on ith place has 0 then you have to XOR it with key that has ith bit = 1

saying simply, for bit B you need !B

Another obvious thing is that higher order bits are more important than lower order bits.

That is:

  • if 'a' on highest place has B and you have found a key with highest bit = !B
  • then ALL keys that have highest bit = !B are worse that this one

This cuts your amount of numbers by half "in average".

How about building a huge binary tree from all the keys and ordering them in the tree by their bits, from MSB to LSB. Then, cutting the A bit-by-bit from MSB to LSB would tell you which left-right branch to take next to get the best result. Of course, that ignores PI/QI limits, but surely would give you the best result since you always pick the best available bit on i-th level.

Now if you annotate the tree nodes with low/high index ranges of its subelements (performed only done once when building the tree), then later when querying against a case A-PI-QI you could use that to filter-out branches that does not fall in the index range.

The point is that if you order the tree levels like the MSB->LSB bit order, then the decision performed at the "upper nodes" could guarantee you that currently you are in the best possible branch, and it would hold even if all the subbranches were the worst:

Being at level 3, the result of

0b111?????

can be then expanded into

0b11100000
0b11100001
0b11100010

and so on, but even if the ????? are expanded poorly, the overall result is still greater than

0b11011111

which would be the best possible result if you even picked the other branch at level 3rd.

I habe absolutely no idea how long would preparing the tree cost, but querying it for an A-PI-QI that have 32 bits seems to be something like 32 times N-comparisons and jumps, certainly faster than iterating randomly 0-100000 times and xor/maxing. And since you have up to 50000 queries, then building such tree can actually be a good investment, since such tree would be build once per keyset.

Now, the best part is that you actually dont need the whole tree. You may build such from i.e. first two or four or eight bits only, and use the index ranges from the nodes to limit your xor-max loop to a smaller part. At worst, you'd end up with the same range as PiQi. At best, it'd be down to one element.

But, looking at the max N keys, I think the whole tree might actually fit in the memory pool and you may get away without any xor-maxing loop.

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • the maximum of a^xi is obtained if xi = not(a) since a^not(a) = FFFF; I can find some logic to work with the bits. BUT for that to work the numbers would have to be sorted otherwise I still scan all inputs which I am already doing... – Pandrei Nov 28 '13 at 11:12
  • "if you annotate the tree nodes with low/high index ranges of its subelements" - the problem with that is that at most levels in the tree - assuming random input - the proportion of positions N values span tends towards (N-1)/N... really not a very effective filter. – Tony Delroy Nov 28 '13 at 11:16
  • @TonyD: I understand what you mean, but I focused mainly on the FULL tree version and that the ranges-of-presence to be coherent/filtered by the upper nodes. It is not to behave as "filter" but rathat a "lookup" that covers all N keys. Ie. having keys `1011, 0111, 1100, 0001` in that order the "tree" would be like "*/[*]=>{ 0/[1..3]={ 0/[3..3]=>X, 1/[1..1]=>X }, 1[0..2]={ 0/[0..0]=>X, 1/[2..2]=>X } }". Note that the subnodes' ranges are limited within the range of their parents. – quetzalcoatl Nov 28 '13 at 11:28
  • Now, in order to find best match for A=1000,P=1,Q=3, the lookup would check first !'1' at root node, resulting in selecting 0/[1..3] node since it's 0 and intersects with 1..3 range. Then, next step would be lookup of !0 in the 0/[1..3], so 0/[3..3] would be picked. And so on. If there were no "0" key, then "1" would be picked. If the "0" key's range would fall out of the target PiQi range, then "1" would be picked again. Ranges written in parents guarantee that there are some keys in children and are used to direct the walker to non-empty leaf. The 0/1 branches allow to pick next best value. – quetzalcoatl Nov 28 '13 at 11:30
  • Walking is quitted if a node with range N..N is visited, since that means that for the binary 0101010 prefix of that node, there is exactly one such key, and since at each **more significant** (bit) level you took the best branch, it must be the best. And it must be in-range, since you always picked a branch which range was intersecting with the target range. The final node might be at tree-depth of 32 at most, but the tree's max size is not 2^32 (whoo:)!) since there's limit for number of keys.. – quetzalcoatl Nov 28 '13 at 11:39
  • If you see any gaps in this, please share it! As I said, I have not analyzed it very thorougly, but I've covered some basic sanity checks, and a full tree as prefix lookup seems possible, even with its pessimistic 32 jumps for each case (which is a large margin from average since max-keys << 2^32). – quetzalcoatl Nov 28 '13 at 11:41
  • the binary tree approach is a good one and is one of the best ideas for solving this problem; however it requires a special kind of BST - Building a segment tree and getting the result using range queries. By far the most problematic part is the fact that even though the input array is the same for all Q test cases, for each test case a can be different making it difficult to use previous results since you need to compute max(a^x[i]) ,1<=i<=N; – Pandrei Nov 29 '13 at 11:11
  • Oh no, don't get me wrong. For each "big test case" where the whole keyset may change, you **have to** discard the old one and build a brand new lookup tree for that new keyset. I don't see much gain in reusing the results from **previous big test case**, but reusing some results amongst all small Q-tests **within current big test case** is the key. Sorry if I was not clear about that. Reusing results or structures between the test cases would add really very much complexity, I think. Rebuilding the tree once every 50k queries seems ok for me and should still result in a boost. – quetzalcoatl Nov 29 '13 at 12:08
0

I've spent some time google-ing this problem and it seams that you can find it in the context of various programming competitions. While the brute force approach is intuitive it does not really solve the challenge as it is too slow. There are a few contraints in the problem which you need to speculate in order to write a faster algorithm:

  • the input consists of max 100k numbers, but there are only 32768 (2^15) possible numbers
    • for each input array there are Q, max 50k, test cases; each test case consists of 3 values, a,pi,and qi. Since 0<=a<2^15 and there are 50k cases, there is a chance the same value will come up again.

I've found 2 ideas for solving the problem: splitting the input in sqrt(N) intervals and building a segment tree ( a nice explanation for these approaches can be found here )

The biggest problem is the fact that for each test case you can have different values for a, and that would make previous results useless, since you need to compute max(a^x[i]), for a small number of test cases. However when Q is large enough and the value a repeats, using previous results can be possible.

I will come back with the actual results once I finish implementing both methods

Pandrei
  • 4,843
  • 3
  • 27
  • 44
  • Wow, there are only 32k unique numbers? How did I miss that? I really would go for a `prefix tree`. I see no immediate gain from using it when your item set is constant. And, actually, I see no gain in using it for a lookup.. What I proposed is a mix of a core of `prefix tree` (which finds you the solution) with annotation about 'segments' (which filters-out keys that are out-of-range). – quetzalcoatl Nov 29 '13 at 12:20
  • there are only ~32k unique values for a, but you need to compute max(a^x[i]); if you were looking for only max(x[i]) than you could build only one tree. – Pandrei Nov 29 '13 at 12:23
  • huh.. I don't get at all what yo uare talking about, sorry. It seems like both I and you are focused on our own solutions :/ I mean, I don't see why it is not possible to build one lookup tree for all 'keys'. A prefix tree. Just to "store" the keys instead of an array. And to allow to READ the value closest to `~A` that lies in Pi-Qi range. A prefix/lookup tree based solely on the value of keys. – quetzalcoatl Nov 29 '13 at 12:27
  • However, with nonunique keys I actually sense a problem as to how to properly annotate the prefix tree.. – quetzalcoatl Nov 29 '13 at 12:33
  • any solution you choose (splitting into intervals or building a tree) is good only if it stores a^x[i], otherwise you have to compute the values each time and it's as good as scanning the whole range [pi,qi]. Remember the input values are not sorted and sorting them would mean loosing the indexes or having to look them up each time. – Pandrei Nov 29 '13 at 12:34
  • How do I have to compute the values each time?! From the start I know that the ~A is the best, I compute it once simply by negating it. And from this point of time I only walk the tree, bit by bit, MSB to LSB, to check what is the closest approximation of that ~A within the PiQi range. If values are 16 bit, that's 16 nodes to visit. Nothing like scanning all X[i]s. – quetzalcoatl Nov 29 '13 at 12:37
  • looking for ~a because that would give the maximum or the closest value to it that would give the max result is not really that easy to obtain; you cant simply take the value ~a. (a=10 we want 5 not 7FF5) so you have to do some costly bit operations. I tried this approach last night and the result was worst than the brute force approach – Pandrei Nov 29 '13 at 12:40
  • 1
    And this is exactly what a prefix tree would help you with. Let's take that a=10. Let's say, unsigned and 8 bit for simplicity. A=0x0A, so ~A is 11110101. Let's say that of course, there's no such key. We have keys of 10110000, 01000101, 11110000, 01110101. If you build a prefix tree for that, after "cutting" the firstmost '1' from ~A=[1]1110101, you'd visit the root node of the tree and pick the '1' path. It direct you to a subtree of " 10110000, 11110000 ". Then, cutting next bit of ~A=1[1]110101 and visiting deeper into that subtree would yield you 11110000. Which is THE answer. – quetzalcoatl Nov 29 '13 at 12:46
  • Were the tree deeper, you'd need to take more bits of ~A and look deeper. And if the tree were annotated with ranges-of-presence, you could walk down the tree respecting the PiQi target ranges. – quetzalcoatl Nov 29 '13 at 12:47
  • If the interval was the same every time, this solution would actually work; but since the interval varies it means you would have come up with a way of figuring out if ~a is actually in the range and if not which the closes number to in that is - and that you cannot to with the prefix tree approach – Pandrei Nov 29 '13 at 12:53
  • I've just deleted that "that's irrelevant (...)" comment. I still cannot decide if the duplicate keys invalidate my idea or not. There's a small "gap" I cannot untangle. The more I think about that, the more I think that the duplicate keys are of no importance in that approach, but I smell something bad somewhere and I cannot put my finger on it. – quetzalcoatl Nov 29 '13 at 12:57
  • Pandrei: yes. This is because I only now described the PREFIX part of the tree. Please now annotate each tree node with "range of presence" like I described in my original answer's and comments. – quetzalcoatl Nov 29 '13 at 12:58
  • For keyset of 10110000, 01000101, 11110000, 01110101, the prefix-presence tree would be `[1]/(0..2) -> [subtree] | [0]/(1..3) -> [subtree]` where the 0..2 indicates the `range of presence of keys with MSB=1` and (1..3) indicates the presence of MSB=0 – quetzalcoatl Nov 29 '13 at 13:00
  • If you now look for ~A=[1]1110101 in range PiQi=(3..4), then you know you CANNOT pick the best left tree node, because the tree node says it has nodes in range "0..2" and it does not intersect with target 3..4. You must hence pick the [0] as the best-available-MSB-in-the-3..4-range and go deeper. – quetzalcoatl Nov 29 '13 at 13:02
  • 1
    Then, the `subtree` picked in that way would be `[1]/(1..3) -> subsub | [0]/() -> empty`, so you pick the left this time. (solution so far: 01??????). Next subtree is `[0]/(1..1)-> 01000101 | [1]/(3..3) -> 01110101` so you pick the right one, and so the path 011????? finds the leaf of 01110101. Note that in the last step I rejected the "left" both because it was worse (had 0 instead of 1) and because it was out-of-range: (1..1) instead of (3..4) – quetzalcoatl Nov 29 '13 at 13:05
  • heh. and after thinking about this, I think the duplicate keys are not dangerous. It'd just end up in having a X-Y (X!=Y) range at the very bottom of the leaf. If the walker wend down to that node, it means that X-Y intersects with Pi-Qi, which means that the "best of possible solutions" we've just found **is in range**. But we don't know precisely where, but that knowledge is not needed anyways! Phew. – quetzalcoatl Nov 29 '13 at 13:12
  • Sorry, I've got to go. I think I've spammed you with too much text anyways. I hope I have not made any mistakes, it was rather hastily prepared (as you see from my suprise at the 32k/16bit value range..). Good luck! – quetzalcoatl Nov 29 '13 at 13:13
  • Well this brings you back to the segment Tree solution, only with the twist that you would build only one tree instead of Q. it's worth trying, I'll give you that :) – Pandrei Nov 29 '13 at 13:14
  • 1
    I started working on the implementation for this and I realized there might be a problem: when adding prefix-presence, because duplicate keys the prefix - presence could expand in such a way that you cannot choose a path anymore, because any path you choose, it's prefix-presence includes the interval of interest (e.g) for bit i=1, you have the following [0]/(1..15), [1]/(2..13) and PiQi = (6,7). So in this case will choose 1, and that can lead nowhere because further down the line there are only values from (1..5) and (10..13) – Pandrei Dec 03 '13 at 12:28
  • If I unserstood you correctly, this is intended! I'm speaking in 8bits instead of 16. In the case you provided, let's say the current i=1 be the 4th bit. Let's say the best solution is ABC1EFGH (4th is 1). As we are now analyzing bit 4, then bits 1..3 are already visited/solved. So, the solution so far is A'B'C'?????. Now, in the case you gave, having PiQi=(6,7) and best bit=1, you pick [1]/(2..13) since it is in PQ range and the best, the next solution approximation is A'B'C'1???? and we descend. Now, we are see (1..5) and (10..13) against (6..7) and we want "E". – quetzalcoatl Dec 03 '13 at 13:27
  • Depending on the value of E we want 0 or 1. However, due to the PQ restriction, we may not be able to pick the BEST. If so, anything in range must be picked. If we wanted E=0 and [0]/(1..5), then we MUST pick the other branch - simply because there are NO numbers with E=1 in the PQ range. As if they "not existed" at all. Hence, our next approximation solution is A'B'C'10??? and we descend further down, until fully completed. – quetzalcoatl Dec 03 '13 at 13:29
  • The trick is that PQ-range says that keys-out-of-range don't exist. So, if we must _always_ abide the rule of "descend-always-into-valid-range" and only then "pick the best". Else, we have to pick the other one (which must be in range due to parent node's range) - because there is nothing else. So, the best-key-**from-PQ-range** must have A'B'C'10??? form, even if that 0 is not best universally - it is best in that range. – quetzalcoatl Dec 03 '13 at 13:39
  • Let's say it's down to the last 2 bits (the rest are resolved);~a=xxxxxxx01. PiQi =(6,7); So the options are [0]/(1..15) and [1]/(2..13); We pick [0] since it matches our bit. now for the last bit we are left with [0]/(1..5) and [1]/(8..15)...and we're left with no option. It is plausible (if you build an example you'll see). Plus, this situation can happen with any of the more significant bits. – Pandrei Dec 03 '13 at 13:40
  • NO, it is not possible. For it to be possible, you must have annotated PARENT nodes wrong! – quetzalcoatl Dec 03 '13 at 13:41
  • If you are at bit 3rd from the end, you are in situation of ~a=xxxxxxB?. Now you pick left/right node depending on the presence. Let's say the pick is 0 and the Presence says (1..15) as it would be in that example. So, you know that a Key of xxxxxx0? **EXISTS** in the range of 1..15. If it exists, then it has some further bits. If it has further bits, then after bit B there WILL BE either ZERO, or either ONE. So, in the subtree, either left or right branch MUST have a presence covering indexes 6 and 7 – quetzalcoatl Dec 03 '13 at 13:44
  • So, if you have a tree node of 0/[1..15] then the union of subnodes's range must add up to full 1..15. At the very last leaves, the last choice will meet a nonitersecting range. So, if the parent had 1..15 then at the leaf, a 0/(1..5) 1/(6..15) is possible but 0/(1..5) 1/(13..15) is not! Otherwise, it the parent's annotation would say that at index 6,7,8,9,10,11,12 there are keys of correct prefix and .. no other bits. – quetzalcoatl Dec 03 '13 at 13:50
  • Sorry for using caps and bolds, but I'm in hurry and failed to invent how to emphasize important bits in a way nicer for the eyes :/ – quetzalcoatl Dec 03 '13 at 13:51
  • One of us is missing something :) I say it can happen because the input ARE NOT ORDERED: you can have xArray[5] = xArray[1000] = 3; So when you add the range presence to the tree the intervals can become very wide. Because the ideal node ~a can exist in the array, but is outside PiQi you can go down the wrong path! – Pandrei Dec 03 '13 at 13:52
  • The'wideness' of the presence-range intervals is granted to be high due to duplicates. But still, even if insanely wide, it will not lie. And you know that at the very leaf it must be disjoint, right? – quetzalcoatl Dec 03 '13 at 13:55
  • I mean, sorry, not disjoint, duplicates, eh – quetzalcoatl Dec 03 '13 at 13:56
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/42412/discussion-between-quetzalcoatl-and-pandrei) – quetzalcoatl Dec 03 '13 at 13:56
  • Maybe I misunderstood; correct me if I'm wrong bu if xArray[0] = xArray[14] = 3, it means that the presence for the node 0x03 will be(0..14), and the same for the parents of this node, assuming N=15; – Pandrei Dec 03 '13 at 13:58