Блог пользователя polosatic

Автор polosatic, история, 4 месяца назад, По-русски

Недавно я решал эту задачу. Очевидно, тут нужны компоненты сильной связности. Я их написал и получил TL14: 277474774.

Я знал, что пушбэки в 1e6 векторов очень медленные из-за динамической реаллокации. Я также знал единственный способ исправить это: предподсчитать размер каждого вектора, и интерепретировать vector<vector> как один статический массив. Реализовывать это было неприятно, но я справился и получил TL38: 277476691

Я пытался добавить разные оптимизации: убирал последний вектор Эйлерова обхода, добавлял прагмы, но решение не проходило 38 тест. Поэтому, у меня возникло 2 вопроса для самых опытных оптимизаторов:

  1. Есть ли другой, более простой способ ускорить пушбэки в векторы, без предподсчета размера каждого вектора?

  2. Почему мое решение падает по TL и как его ускорить? Ограничения до $$$10^6$$$ должны влезать в 2.5 секунды.

UPD: #define vector basic_string — очень хорошая оптимизация. EDIT: не используйте этот define если пользуетесь vector<vector<int>> vec вместо vector<int> vec[N].

UPD2: нашел этот блог. Там объяснены нюансы.

  • Проголосовать: нравится
  • +46
  • Проголосовать: не нравится

»
4 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

If you know beforehand approximately how much space you need, you can probably call std::vector::reserve

  • »
    »
    4 месяца назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Much time ago I have the same issue and tried vector.reserve(), but it also gave me TL. But that time precomputing of all sizes helped my solution to pass.

    • »
      »
      »
      4 месяца назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Well, vectors in general is slower than static array, I'm not very sure about the specifics of it but seems like it's memory allocation mechanics are more complicated

»
4 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Probably not the most useful idea, however does emplace_back work?

  • »
    »
    4 месяца назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    I tried it when I met this issue in the first time. It also did not work, same as vector.reserve().

  • »
    »
    4 месяца назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Despite the way of constructing an object, push_back actually has the same implementation as emplace_back.

»
4 месяца назад, # |
  Проголосовать: нравится +39 Проголосовать: не нравится

Mushrooms function works in sqrt(x)/2

For the vectors, the fastest way is to use something similar to bucket sort, which is to merge all vectors into one array.

»
4 месяца назад, # |
  Проголосовать: нравится +16 Проголосовать: не нравится

My vector filled copy paste code for SCCs ACd in around half of the TL, so that probably isn't the cause unless there's some compiler specific wizardry going on. I'm pretty sure the comment above hit the spot: the mushrooms function is the cause.

»
4 месяца назад, # |
  Проголосовать: нравится +34 Проголосовать: не нравится

Use basic_string instead of vector. They are exactly the same, but basic_string is faster. (though the optimization isn't significant)

  • »
    »
    4 месяца назад, # ^ |
    Rev. 3   Проголосовать: нравится +10 Проголосовать: не нравится

    Wow! This is the thing I wanted to know. I added #define vector basic_string and it runs in 1186ms instead of 1625! It is 37% faster with just one define! I have never heard it! Before rewriting vector to one array, I will first try this optimization.

    278616547

    UPD: do not use this define if you use vector<vector<int>> vec instead of vector<int> vec[N].

    • »
      »
      »
      4 месяца назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      27%

      • »
        »
        »
        »
        4 месяца назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        1625/1186 = 1.37015177065767284992 1186/1625 = 0.72984615384615384615

        • »
          »
          »
          »
          »
          4 месяца назад, # ^ |
            Проголосовать: нравится +1 Проголосовать: не нравится

          Yes, but still 27%, not 37%.

          • »
            »
            »
            »
            »
            »
            4 месяца назад, # ^ |
            Rev. 4   Проголосовать: нравится +36 Проголосовать: не нравится

            "A is $$$p\%$$$ faster than B" means "the speed of A is $$$p\%$$$ greater than one of B", or, equivalently, $$$v_A = \left(1 + \frac{p}{100}\right)v_B$$$ where $$$v_A$$$ is the speed of A and $$$v_B$$$ is the speed of B. The most natural way to define the speed of a solution is, given it's repeated several times, say that it is equal to its execution frequency, thus $$$v_A = \frac{1}{t_A}$$$ and $$$v_B = \frac{1}{t_B}$$$. So we get $$$\frac{1}{t_A} = \left(1 + \frac{p}{100}\right)\frac{1}{t_B}$$$ and $$$p = \left(\frac{t_B}{t_A} - 1\right) \cdot 100$$$, so $$$p = \frac{1625\text{ ms}}{1186\text{ ms}} \approx 37$$$ is the correct approach.

            Taking $$$p = \left(1 - \frac{t_A}{t_B}\right) \cdot 100$$$ does not really make sense here, because, for instance, when we say that A is $$$100\%$$$ faster than B, we mean that A is twice as fast as B rather than that A is momentary.

            One can say that $$$1625\text{ ms}$$$ solution is $$$27\%$$$ slower than $$$1186\text{ ms}$$$ solution, it is fine. And if someone says that B is $$$100\%$$$ slower than A, they do mean that A is momentary (or B is infinitely long).

»
4 месяца назад, # |
  Проголосовать: нравится +9 Проголосовать: не нравится

Use this.

pAAst3Q.png

»
4 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

I suggest you also run a profiler on your solution to know exactly what you can optimize. Sometimes things take longer than you expect, even when asymptotics is ok.

I used samply several times, for example.

»
4 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Наверное дело в самом решении. Я открыл своё старое полное решение, перепослал под c++20 и оно зашло за ~1150ms 278628577. Там и обычные вектора и sqrt, но другой алгос конденсации (через один dfs)

  • »
    »
    4 месяца назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    Нашли выше уже, mushrooms работает не за единицу, как должен, оттого и тл. Никаких хитрых оптимизаций не нужно было.

»
4 месяца назад, # |
  Проголосовать: нравится +7 Проголосовать: не нравится

push_back isn't slow, push_back is exactly as fast as intended. Reallocating memory according to your design is slow. You needed to redesign it, which is the standard solution to this problem.

»
4 месяца назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

Allocation-friendly way to store graphs: https://mirror.codeforces.com/blog/entry/5195
No need for 2d-vectors.

»
4 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

You can check out my submission here, I guess ? 278711068

»
4 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

can you please explain why vector push-back takes more time