boleyn.su's blog

By boleyn.su, 11 years ago, In English

This problem is easy to solve using other languages with or without the help of segment tree.

But as I am learning Haskell, I tried to solve it with segment tree using Haskell. However, after a lot of tries, I found it impossible for me to solve it. So I need your help. I would be glad if you can point out any error in my submission or you can show me a Haskell solution (with the help of segment tree that support updating an interval) to this problem or let me know why it cannot be solved this way.

Thanks!

UPD1: My submission: here

UPD2: Another submission of mine (after reading Data.SegmentTree): here

UPD3: slycelote's submission(Accepted. However, I think it doesn't take the advantage of Haskell.): here

  • Vote: I like it
  • +31
  • Vote: I do not like it

»
11 years ago, # |
  Vote: I like it +22 Vote: I do not like it

The first thing that I see is that you're using Strings for input. Haskell Strings are linked lists of Unicode characters and are unsuitable for processing large amount of data. You should use lazy Bytestrings instead.

The second thing that is immediately evident is memory usage. Your program takes 200 megabytes on a test case with n < 104. That's due to excessive laziness: during program execution unevaluated thunks are accumulated and eat up all the memory. (Look here for a simple example.) The solution if to throw in some strictness annotations and introduce strict fields into your SegmentTree data type. And while we're at it, let's add GHC option -funbox-strict-fields, which basically replaces Ints (that are equivalent of java.lang.Integer) with Int#s (that are raw machine integers). (Warning: in general, you need to be careful with these two optimizations: they aren't default for a reason and can make performance worse.)

We get the following code: 4060212. (Apologies for Codeforces' messed up syntax highlighting.) It's now using only 2 megabytes of memory, which is good, but it still fails the same test case. There's definitely something very wrong here, so it seems that it's time to actually read problem statement and the code :)

You're implementing a segment tree with updates on sub-segments. For this to be efficient, updates to children nodes must be postponed until they are actually needed. Consider, for example, two consecutive updates of the same node: first with the value 1, then with the value 2. You want only the second update to be propagated to children. You may think that due to Haskell's laziness it's done automatically, but it's not… In the first update children of the node are replaced with thunks. In the second update they are replaced with new thunks, but they must reference old ones. When you actually need the value of a child, you'll have to unpack the whole Russian nested doll of thunks. So updates to children are postponed but not accumulated. Maybe it's possible to employ Haskell's laziness, but I don't know how.

We need the same implementation as in an imperative program: add a new field to your SegmentTree node, that will hold the value that this node has been updated with, but not yet propagated to its children. Because you're storing the maximum on sub-segment, the value itself doesn't need to be stored (it's equal to the sub-segment maximum), so we need only Bool flag that will be set to True if there is an unpropagated update waiting at this node. Here is the new version that passes system tests: 4065385. Note two things: 1. children are updated only in one case for update function; 2. query got a new case for when the flag is True (then the whole sub-segment contains only one value, and there's no need to descend to children). I also took the liberty of removing l and r fields from your SegmentTree node: they never change and we can compute them on the fly. In an imperative program it doesn't matter, but in a functional one new nodes are created all the time, so constant fields introduce unnecessary copying.

Finally, I'm a Haskell beginner as well, so take everything I said with a grain of salt :)

  • »
    »
    11 years ago, # ^ |
    Rev. 2   Vote: I like it +2 Vote: I do not like it

    Thanks for your replying and spending time to solve this problem. I am happy that you finally get this problem passed the system test. However, your solution is not what I want. It seems like how I implement Segment Tree in languages like C++. It doesn't take the advantage of Haskell's features.

    BTW, I DO learn something in your code, like using ! to declare strict fields(I didn't know what are strict fields before).

    • »
      »
      »
      11 years ago, # ^ |
        Vote: I like it 0 Vote: I do not like it

      My implementation uses an infinite list of integers, does that count as a Haskell feature? :D

      Seriously though, I don't think programming contests is the area where specifics of different languages play a big role. Can you find a problem which is particularly easy to write in, say, Java or C++? I can, but it has to do with their standard libraries, not with the languages themselves.

      Now I would understand your frustration about the solution if it was implemented effectively in imperative style ('You can write in Fortran in any language'). However that's definitely not the case here.

      • »
        »
        »
        »
        11 years ago, # ^ |
          Vote: I like it 0 Vote: I do not like it

        Thanks for your replying.

        Everything in your code is good except that you used a Bool to make the segment tree lazy while I want to do so just with the help of Haskell's features. In fact, what I want may be impossible and your way (use a Bool) may be the only way to achieve my goal. Thus, your code is not what I want doesn't mean it is not good.

»
11 years ago, # |
  Vote: I like it 0 Vote: I do not like it

Here is my solution written on Haskell without Segment Tree http://codeforces.com/contest/273/submission/4086244. In any case, could you please describe what do you mean by "advantage of Haskell"? If you mean cool type system, laziness or declarative programming, all solutions mentioned above use it. Of course, they don't use features like functional dependencies and meta-programming, but it's okay, because a common and in general fruitful way is to go from solution to language, not in backward direction.

  • »
    »
    11 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    When I say the advantage of Haskell, I mean the laziness. (Because segment tree that supports interval updating and querying needs lazy tag if written in languages like C++, and I want to implement it without lazy tag in Haskell.)

»
7 years ago, # |
Rev. 4   Vote: I like it 0 Vote: I do not like it

Hey there,

I came across your post while searching for "haskell segment tree" (I'm pretty sure many others do too and hope this helps). I also feel unsatisfied with the explicitly lazy version of segment tree by slycelote. After many tries I've finally found the space leak in your submissions (and my previous submissions). In the lazy propagation case of update, we don't really need the left and right children. What we are trying to do here is to build a new sub-tree which resembles the current sub-tree, but with all values set to x (the updating value). As the shape of a sub-tree is fully determined by the segment covered by its root, we can ignore the left and right children and lazily build the new sub-tree using just the segment. However, flag is not totally useless here: we can still employ it to break early in the query function: if flag is True, all nodes in the current sub-tree have the same value and we don't have to dig deeper.

Here's the submission that doesn't use flag (it's there but always False): http://codeforces.com/contest/273/submission/29282195

And here's the version that use flag (see the make function): http://codeforces.com/contest/273/submission/29282263

  • »
    »
    7 years ago, # ^ |
    Rev. 2   Vote: I like it -9 Vote: I do not like it

    WTF ? this blog is for 4 years ago !!!!

    R U using internet explorer ? :\

    • »
      »
      »
      7 years ago, # ^ |
        Vote: I like it +5 Vote: I do not like it

      Well, this "unanswered" question happens to be on the first page on Google search and I happen to be learning both Haskell and segment tree so it's never too late :)