OtterZ's blog

By OtterZ, history, 9 hours ago, In English

When finding the contribution of a straight line in a problem and seeking the maximum or minimum value, some targeted algorithms are usually used to solve it. If you find a contribution formula related to the straight line formula and need to find the maximum or minimum value among the contribution values, consider the following algorithms.

1. Li Chao Segment Tree

1. Algorithm Explanation

The Li Chao segment tree is a data structure for handling straight line problems derived from the segment tree. The operations it can handle are:

  • Query the maximum or minimum value at a single point;
  • Add a straight line to an interval.

We build a segment tree for the interval (a segment tree with dynamic node creation is more common). Each node stores only one straight line (slope + intercept). For a single-point query, we traverse the points on the path from the root to the leaf node corresponding to the subscript, calculate the results corresponding to the stored straight lines, and make contributions. For adding a straight line, first find the corresponding nodes on the segment tree for the interval where the straight line is located, and then update a single node to be updated according to the following operations:

  • This node retains the straight line with the better value at the midpoint of the interval and passes the other straight line down.
  • If the straight line to be passed down is better at the left boundary of the interval, it is used to update the left child.

  • If the straight line to be passed down is better at the right boundary of the interval, it is used to update the right child.

  • If a vertically upward straight line appears, it is generally processed as a straight line with a slope of 0 and an intercept equal to the optimal point on the straight line.

It can be found that at most one of the operations in $$$2$$$ and $$$3$$$ will update the child. It can be concluded that the worst-case time complexity for a single-point update is $$$\operatorname{O}(\log_2 n)$$$. Any interval will correspond to $$$\operatorname{O}(\log_2 n)$$$ nodes, and the time complexity for adding a straight line to any interval is $$$\operatorname{O}(\log_2^2 n)$$$. However, when optimizing the straight line transfer, mostly only global addition is used, and only the root node needs to be updated, resulting in a time complexity of $$$\operatorname{O}(\log_2 n)$$$.

2. Advantages

  • Easy to use: Optimizing the straight line transfer with the Li Chao segment tree does not require ensuring the monotonicity of the straight line slope. It is straightforward to apply in implementation and supports persistence, merging, and splitting of the segment tree. When the number of operations is limited, it can be brutally cleared, which is very useful.
  • Code difficulty: The template of the Li Chao segment tree is similar to that of the segment tree. It is very simple for contestants who have mastered the segment tree proficiently. And the segment tree is a high-frequency knowledge point in competitions at the advanced level and above. Therefore, the code difficulty is not high for many contestants.
  • Efficiency: Compared with the CDQ divide-and-conquer method, the Li Chao segment tree is in most cases $$$\operatorname{O}(\log_2 n)$$$ times faster in efficiency.

3. Limitations

Using the Li Chao segment tree requires ensuring that the number of points for which the optimal value is sought is limited when the straight line formula makes contributions, and it cannot support deletion.

4. Problem

CodeForces1179D

2. Monotonic Queue and Convex Hull

1. Algorithm Explanation

For the situation where the slopes of the straight lines added are monotonically non-deteriorating in the order of addition and the query coordinates are monotonically increasing, consider using a monotonic queue to obtain:

  • When adding to the end of the queue, consider the magnitude of the abscissa of the intersection point of the added straight line and the last straight line and the abscissa of the intersection point of the last straight line and the previous straight line. If the former is smaller, delete the last straight line.
  • If the contribution of the straight line at the head of the queue is worse than that of the next one, delete the head of the queue.

Operation $$$1$$$ is continuously performed before insertion until the last element does not meet the deletion condition. Operation $$$2$$$ is continuously performed before insertion until the head of the queue does not meet the addition condition. In this way, we obtain a linear solution for this type of straight line transfer problem. If we explain the problem in another way: - Insert nodes, and the abscissas are monotonically increasing; - Query the intercept of the straight line with the largest slope passing through one of the points.

Then the solution is the convex hull. Also, maintain a double-ended queue. When inserting, delete the end of the queue when it is found to turn upward.

The two methods have different explanations but the same essence. You can choose according to your preference. For the situation where the query points are not necessarily monotonically increasing, consider not deleting the head of the queue and perform a binary search during the query, which will add an additional $$$\operatorname{O}(\log_2 n)$$$. For the situation where the slopes of the straight lines are not necessarily monotonically increasing, consider adding the CDQ divide-and-conquer method. Sort the left half according to the slope each time to update the right half, and the time complexity is $$$\operatorname{O}(n\log_2^2 n)$$$.

2.Advantages

  • Code difficulty: The queue part is easy to write, and the CDQ divide-and-conquer is not difficult to write either;
  • Efficiency: The insertion and deletion of the queue are amortized linearly.

3.Disadvantages

For the situation that requires the CDQ divide-and-conquer method, it needs to be offline, and it may be inconvenient to apply for complex problems.

4.Example Problem

SPOJ2940

3.Summary

Different algorithms have different advantages and disadvantages, and different algorithms should be used for different situations. In the problem of the maximum or minimum contribution of a straight line, we find that the Li Chao segment tree and the monotonic queue can help us solve this problem quickly and further handle more complex dynamic programming problems.

Full text and comments »

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

By OtterZ, history, 4 weeks ago, In English

 I'm calling MikeMirzayanov to solve that.

Full text and comments »

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

By OtterZ, history, 5 months ago, In English

Here are some useful conclutions for naive algorithms to solve number theory problem,I hope you can know something about it and solve number theory problems more easily.

1.The number of prime factors of an integer

It's sure that the number of prime factors of an integer is very small,and an integer $$$v$$$ can be the product of at most $$$\log_2(v)$$$ primes ($$$2 ^ k$$$ the worst).This can be used for bruteforce and State compression.

Thanks AkiLotus to remind me that for the number of distinct prime factors of a integer $$$\operatorname{w}(n)$$$,$$$\sum_{i = 1}^n \operatorname{w}(n)$$$ is $$$\operatorname{O}(n \log \log n)$$$.

example:510D.

2.The number of factors of an integer

First of all,$$$\sum_{i = 1} ^ n \operatorname{d}(n) = \sum_{i = 1} ^ n [\frac{n}{i}] \approx n \ln n$$$.

Then I've found out that the number of factors of an integer($$$\operatorname{d}(n)$$$) is usually small,and to make sure,I made a code to get the maxinum number of the number of factors,and get:

  1. For $$$n \le 10 ^ 4,\operatorname{d}(n) <= 64$$$;
  2. For $$$n \le 5 \times 10 ^ 4,\operatorname{d}(n) <= 100$$$;
  3. For $$$n \le 10 ^ 5,\operatorname{d}(n) <= 128$$$;
  4. For $$$n \le 2 \times 10 ^ 5,\operatorname{d}(n) <= 160$$$;
  5. For $$$n \le 3 \times 10 ^ 5,\operatorname{d}(n) <= 180$$$;
  6. For $$$n \le 5 \times 10 ^ 5,\operatorname{d}(n) <= 200$$$;
  7. For $$$n \le 10 ^ 6,\operatorname{d}(n) <= 240$$$;
  8. For $$$n \le 5 \times 10 ^ 6,\operatorname{d}(n) <= 384$$$;
  9. For $$$n \le 10 ^ 7,\operatorname{d}(n) <= 448$$$;

So if your solution of a problem is $$$\operatorname{O}(n\max \operatorname{d}(a_i))$$$ or $$$\operatorname{O}(\sum \operatorname{d}(a_i))$$$,it might be correct because for $$$a_i \le 10 ^ 7$$$,it's sure that $$$\operatorname{d}(a_i) \le 500$$$.

examples:

3.Euler's Function: $$$\operatorname{O}(\log_2 n)$$$ times to $$$1$$$.

It's sure that $$$\phi(n) \le \frac{n}{2}$$$ for $$$2 | n$$$,and $$$2 | \phi(n)$$$ for $$$n > 1$$$.So if you use operation $$$x = \phi(x)$$$ for $$$x = n$$$ initially,it will become $$$1$$$ in $$$\operatorname{O}(\log_2 n)$$$ times.

In fact,it can be related to problems about power,when we try to solve the problem using Euler's Theorem or Extend Euler's Theorem.

example:

4.Prefixes: $$$\operatorname{O}(\log_2 n)$$$ distinct prefix great common diverse/and/or

Thanks Ghulam_Junaid and Timosh to remind me about the feature.

For $$$\gcd(a_1,a_2,...,a_k)$$$,We can add a new integer $$$a_{k + 1}$$$ and found:

  • If $$$\gcd(a_1,a_2,...,a_k) | a_{k + 1}$$$,it's sure that $$$\gcd(a_1,a_2,...,a_k,a_{k + 1}) = \gcd(a_1,a_2,...,a_k)$$$.
  • Otherwise,$$$\gcd(a_1,a_2,...,a_k,a_{k + 1}) \le [\frac{\gcd(a_1,a_2,...,a_k)}{2}]$$$.

So there are at most $$$\log_2 n$$$ distinct prefix great common diverse.

For operator and or or,every integers can be written by $$$\log_2 n$$$ digits,and:

  • For operator and,the number of "1" in the digits decreases;
  • And for operator or,the numbr of "1" increases;

So there are at most $$$\log_2 n$$$ prefixes and suffixes.

example:

5.At most $$$[2\sqrt{n}]$$$ distinct integers of $$$[\frac{n}{i}],1 \le i \le n$$$.

Known as number theory chunking in public,we can proof that $$$[\frac{n}{i}] = [\frac{n}{[\frac{n}{i}]}]$$$,and then split $$$[1,n]$$$ to $$$\operatorname{O}(\sqrt n)$$$ sections like $$$[l,r = [\frac{n}{l}]]$$$,it's really useful while calculating $$$\sum_{i = 1}^n \operatorname{f}([\frac{n}{i}])$$$ or it's easy to consider several integer $$$v_1,v_2,...,v_k$$$ together when $$$[\frac{n}{v_i}],1 \le i \le k$$$ is the same.

If we use Möbius inversion first,we might found that we have to calculate $$$\operatorname{f}(\frac{x}{d})$$$,using the feature we can found out that we can calculate the function just $$$[2\sqrt{n}]$$$ times for distinct numbers.

example:

Last:written in the end:

I would like to thank:

Full text and comments »

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

By OtterZ, history, 5 months ago, In English

There are some bugs and features exists while compiling a c++ code,it looks weird but really exists on standard c++ compiler.I'll write down the bugs and features here to remind you to aware of them.

1.stl::vector:fail to use operator "=".

I was trying to implent the merge function of segment tree while solving the task,and the initial code is

here

and found that it will get WA on test #6 using the language C++14(GCC 6-32).Then I found out that the

code

might got wrong.That because the location of lc[u] changed after using lc.push_back() and the same as rc[u],so the operator "=" might not work.

The bug have been fixed since C++17(GCC 7-32),but if you compile your code using language C++ and the previous version before C++17(GCC 7-32),the bug still exists.

2.vector:Can vector be compiled by head file ?

I've participated on a school contest and found that a code with stl :: vector ran successfully without head file <vector>,after that,I made some testing and found something miraculous:

If the compiler is on Windows system (on codeforces,Dev c++ on Windows,etc),you can use <algorithm> instead of <vector> to compile stl :: vector,but if the compiler is on Linux system (Luogu,and so on),<algorithm> is unable to compile stl :: vector.Also, in Windows <iostream> is able to compile stl :: vector and it's invalid to use <iostream> to compile stl :: vector in Linux.So,for participants,especially for Chinese who is willing to engage on Olympics in informatics,please be aware of this.

For more such bugs and features,you can reply to the comment and I'll put some of them that sometimes exists but not well-known here.

Full text and comments »

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

By OtterZ, history, 5 months ago, In English

1.Introduction

Binary grouping is a way to implent dynamic insertion.If you found that the query for a data structure constructed by inserted elements from set $$$s$$$ can be answered online and be solved by uniting querys from a set of data structures built on a partition of $$$S$$$ in $$$O(h(n,m))$$$ as $$$m$$$ is the number of subsets in the partition and you can construct a data structure in $$$O(\operatorname{f}(n))$$$,It means that you can divide the elements in groups,for $$$n = \sum 2 ^ {a_i}(a_i > a_{i + 1})$$$,We can divide $$$n$$$ elements into groups,each group has $$$2^{a_k}$$$ elements,in this way we divided elements to $$$\log_2{n}$$$ groups.Then we build a data structure for each group of insertions using the construct algorithm.

When we insert a new element,we divide it in a group,and when we found a group that its size is the same as the new group,we merge the two groups into one group and rebuild a data structure with the elements in the group.Using the method,we get a way to implent dynamic insertion and the method to construct is $$$O(\sum {f(x)})(\sum x = n \log_2 n) \approx O(f(n)\log_2 n)$$$ in total and $$$O(h(n,\log_2 n))$$$ for each query.

2.Examples

It can be used for kd-tree,but I haven't found an example on the site,so please leave a comment if you found the example.

The example of using the method on kd-tree is P4148 on luogu,in the problem,the elements is the points in the first operation and we can use kd-tree to deal with the elements and answer the query online.But we have to implent dynamic insertion,so we use this method.

code

In CF710F,we use the method on AC-Automation.In the task,we use a group to deal with the inserted string and an another group for deleted strings.In both of the groups,we devide the strings into groups and deal with insertions using the method and make ACAM for each group. When we insert a string,we insert it to the group of inserted strings and insert it to the group of deleted string when we delete it.For querys,if $$$x_i$$$ means that How many string is concluded if we arrive index $$$i$$$ while running the ACAM,$$$y_i$$$ is how many string ends here in the trie,we get $$$x_i = y_i + x_{fail_i}$$$,and the way to get answer is:

  1. Run all the ACAMs in the group of inserted strings and add $$$x_i$$$ to answer while arriving index $$$i$$$.
  2. Run all the ACAMs in the group of deleted strings and minus answer by $$$x_i$$$ while arriving index $$$i$$$.

Here is the Accepted submission.

written at the end of the blog

We would like to thank:

  1. Masters who invent the method;
  2. OtterZ edit the blog;
  3. negative-xp to give advice and make the blog better.
  4. adamant suggest a good problem as an example of using the trick.

Full text and comments »

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