Unofficial ACL Beginner Contest Editorial
Разница между en43 и en44, 2,148 символ(ов) изменены
### [A – Repeat ACL](https://atcoder.jp/contests/abl/tasks/abl_a)↵

<spoiler>↵
Easy programming-language-knowledge check typical of the first task of AtCoder beginner contests. The following is a passing Kotlin submission:↵

```↵
fun main() {↵
    val k = readLine()!!.toInt()↵
    val ans = "ACL".repeat(k)↵
    println(ans)↵
}↵
```↵
</spoiler>↵

### [B &ndash; Integer Preference](https://atcoder.jp/contests/abl/tasks/abl_b)↵

<spoiler>↵
Assume there is an integer in both ranges and call it $x$. Note that both $x \ge \max(A, C)$ and $x \le \min(B, D)$ must therefore be true.↵

Thus $x$ exists if and only if $\max(A, C) \le \min(B, D)$ is true. This is the standard formula for finding the overlap of two ranges.↵
</spoiler>↵

### [C &ndash; Connect Cities](https://atcoder.jp/contests/abl/tasks/abl_c)↵

<spoiler>↵
Note that the initial network of cities can be divided into one or more *components*, where any city of a component can reach any other city in that component following roads, but there are no roads connecting components together.↵

If there are $c$ components, Snuke can connect two of them by choosing one city from each component and building a road between them, which combines them to a single component; so the answer would be $c - 1$.↵

Components can be found using breadth-first/depth-first searches, however there's an easier way if you already have a special data structure known as [DSU (disjoint-set union)](https://en.wikipedia.org/wiki/Disjoint-set_data_structure). And there is an implementation available right in the `dsu` module of the AtCoder library. So start with $c := N$, for each road given in the input, you can use `same` to check if the vertices are already connected; if they aren't, `merge` them and decrement $c$.↵

Note that though the input is $1$-indexed, the AtCoder DSU implementation is $0$-indexed.↵
</spoiler>↵

### [D &ndash; Flat Subsequence](https://atcoder.jp/contests/abl/tasks/abl_d)↵

<spoiler>↵
Imagine an array $B$, initially filled with zeros. The answer then can be theoretically obtained with the following algorithm:↵

For each $a$ in $A$:↵

- Find $\displaystyle\max _{j = a-k} ^{a+k} B_j$. Let it be $r$↵
- Set $B_a := r+1$. This works because you are finding the longest subsequence that can include $a$, and then extending it.↵

Then the final answer is $\max B_i$.↵

Unfortunately this is too slow as searching for the maximum in a range naively is $O(K)$, thus making the final complexity $O(NK)$. However, there is a special data structure that solves this problem, called a segment tree.↵

Segment trees work by storing the array data in the leaves of a fixed binary tree, then storing a "sum" in the ancestor nodes of those leaves. Updating a single entry is slower than a normal array as $O(\log n)$ ancestors need to be updated, but querying the "sum" of a range becomes much faster, as only $O(\log n)$ nodes need to be accessed, and the data "summed".↵

The scare quotes around "sum" imply that it's a more general term &mdash; segment trees can indeed implement sums, but more generally, it can implement any [monoid](https://en.wikipedia.org/wiki/Monoid). Basically, it's a closed binary operation $(S \oplus S) \rightarrow S$ that is both associative and has an identity element.↵

As you are only storing non-negative integers, the $\max$ operator indeed has an identity element, $0$. Even if you needed to store negative integers, you can typically set the identity to any number lower than anything else you'd use (just like how you might use a large number as "infinity" in DP/greedy problems)↵

The `segtree` module in the AtCoder library is an implementation of a segment tree. Be sure to read the documentation carefully; you need to set the operator and identity element. Ranges should be entered in half-open form, and be careful of accidentally exceeding the index limits in queries.↵

There is also a way to solve this using only an ordered map.↵
</spoiler>↵

### [E &ndash; Replace Digits](https://atcoder.jp/contests/abl/tasks/abl_e)↵

<spoiler>↵
Precalculate $10^x$ (under modulo, of course) for $0 \le x \le N$. Also precalculate $\displaystyle ones(x) = \sum _{j=0} ^{x-1} 10^j$ for $0 \le x \le N$, representing the value of a string of all ones of length $x$.↵

This problem should remind you a lot of the previous one, however you now have to also update on a range. This can be done using a lazy-propagating segment tree. It's "lazy" because range updates are not all done straight away, but could be "queued" up in an ancestor node, only being "pushed" toward the leaves when needed for queries.↵

There is a `lazysegtree` in the AtCoder library as well. Configuring it for this problem is a little more involved than the previous one:↵

- The monoid operator isn't a simple sum, but requires that the *size* of the segment to be known as well. Thus $S$ should be a tuple $(sum, size)$, with the operator $(sum_a, size_a) \oplus (sum_b, size_b) = (sum_a \cdot 10^{size_b} + sum_b, size_a + size_b)$. Its identity, $e = (0, 0)$. The $sum$ terms should be under modulo.↵

- $F$, the mapping, can be a simple integer indicating the digit to update with, however a "null / identity map" needs to be assigned as well, this could be $id = -1$. $composition(g, f)$ should be $g$ (the newer update is on the left as in the traditional notation $(g \circ f)(x) = g(f(x))$). $mapping(f, s)$ should be $s$ if $f = id$, and otherwise $(f \cdot ones(size_s), size_s)$↵

The rest is then just implementing the task. Note that when initializing the segment tree, the value $(1, 1)$ needs to be set individually to all indices; you can't just queue a $1$ range-update as they start out as the empty segment $(0, 0)$.↵

You can also still solve this with an ordered map.↵
</spoiler>↵

### [F &ndash; Heights and Pairs](https://atcoder.jp/contests/abl/tasks/abl_f)↵

<spoiler>↵
Define a *bad pair* as a pair where the two persons' heights are equal.↵

Let's define a useful function $\displaystyle pairs(n) := \frac{(2n)!}{n!\cdot 2^n}$, the number of ways to make $n$ pairs (good or bad) out of $2n$ people. Explanation: Start with any of $(2n)!$ permutations. When you pair them up, you don't care about the order within the pair, so you divide by $2$ a total of $n$ times. Then you also don't care about the order between the pairs, so you divide by $n!$.↵

The main idea involves the [Inclusion–exclusion principle](https://en.wikipedia.org/wiki/Inclusion%E2%80%93exclusion_principle). 
Let $F_k$ denote the set of sets of exactly $k$ bad pairs, such that within a set, no person appears twice. Let's assume we have a function, $f(k)$, denoting the size of $F_k$. $f(0) = 1$, because there is exactly one set consisting of $0$ bad pairs: the empty set.↵

We want to find


In general, principle of inclusion-exclusion (PIE) can be summarized with the following steps:↵

1. Identify a set of "violations", i.e. a set of rules you want to obey and specific requirements for each rule to be broken.↵
2. Identify a way to calculate $F(S)$: for every subset $S$ of possible violations,
 the number of matchings (of all people) with at least one bad pair; from that we can subtract it from $pairs(N)$ for the final answer.↵

Imagine that we got the bad pairs from $F_1$, and lis
ways to violate *at least* that subset (note that we don't need to know how to calculated them in some arbitrary order. Let $BAD_i$ denote the set of all bad matchings that contain the $i$-th bad pair in that list. We want to find the size of the union of  number of ways to violate exactly that subset).↵
3. The number of ways to obey all rules (i.e. not commit any violations) will then equ
all $BAD$ sets, i.e. $|BAD_{1} \cup BAD_{2} \cup ... \cup BAD_{f(1)}|$ However we only have the sizes of the individual $BAD$ sets, which are all $pairs(N-1)$, cause we've constructed $N-1$ pairs after the fixed bad pair. Thus the sum of their sizes are $f(1) \cdot pairs(N-1)$↵

This however, overcounts the size of the union, as matchings with more than one bad pair would be represented in several $BAD$ sets. However, we can obtain the sum of sizes of pairwise intersections $BAD_i \cap BAD_j$ by fixing *two* bad pairs using the sets from set $F_2$, then filling the rest arbitrarily; the sum is thus $f(2) \cdot pairs(N-2)$ ↵

[noting that if we counted $f(2)$ correctly, we would correctly skip the empty intersections where bad pairs $i$ and $j$ happened to contain the same person]↵

Likewise we can obtain sums of sizes of triple-wise intersections, etc., then using the inclusion-exclusion principle to obtain the formula↵

$$\displaystyle \left|\bigcup _{i = 1}^{f(1)} BAD_i\right| = f(1) \cdot pairs(N-1) - f(2) \cdot pairs(N-2) + f(3) \cdot pairs(N-3) - ... f(N) \cdot pairs(0)$$↵

And note that our final answer,↵

$$\begin{equation}↵
\begin{split}↵
ans & = pairs(N) - \left|\bigcup _{i = 1}^{f(1)} BAD_i\right| \\↵
    &
(\sum _ {|S| \text{ is even}}F(S)) - (\sum _ {|S| \text{ is odd}}F(S)) $ (note that zero is also even)↵

In this problem there are up to $N$ possible violations, i.e. the $i$-th pair of people being of equal height, i.e. a *bad pair*. Note that we don't actually care about the order of the pairs, so we can just push all the "bad pairs" to the beginning, so we actually only have to worry about $O(N)$ possible subsets of violations. Let's define $G(k) = \sum _ {|S|=k}F(S)$, and calculate the final answer as $(\sum _ {k \text{ is even}}G(k)) - (\sum _ {k \text{ is odd}}G(k)) $↵

Let $f(k)$ be the number of ways to match up $k$ bad pairs from the $2N$ given people. We can thus see that $G(k)
 = f(0k) \cdot pairs(N) - f(1) \cdot pairs(N-1) + f(2) \cdot pairs(N-2) - ... f(N) \cdot pairs(0) \\↵
    & = \sum _{i=0} ^N (-1)^i \cdot f(i) \cdot pairs(N-i)↵
\end{split}↵
\end{equation}$$
-k)$, note we don't care about accidentally making more bad pairs, as it's accounted for in the definitions of $F(S)$ and $G(k)$.

[If this is difficult to understand, try working through the ["Counting derangements"](https://en.wikipedia.org/w/index.php?title=Inclusion%E2%80%93exclusion_principle&oldid=973229648#Counting_derangements) example in the Wikipedia article, which describes an analogous but simpler situation of "fixing" matching cards like we do with bad pairs here]↵

Now let's figure out how to find $f(k)$. First note that the exact heights don't matter, only their equality. We can divide the people into *components* with the same height as each other. Collect all component sizes into array $a$. For example, if $h = [1, 1, 1, 2, 2, 4]$, then $a = [3, 2, 1]$. This can be easily done using sorting and run-length compression.↵

Then from each component $a_i$, construct array $b_i$, indexed over $[0, \lfloor a_i/2 \rfloor]$, where $b_i[j]$ denotes how many ways are there to fix $j$ bad pairs from this component. $\displaystyle b_i[j] = \binom {a_i}{2j} \cdot pairs(j)$, since we take $2j$ people from the component and make $j$ pairs out of them.↵

In the case of a single component, $f(j) = b_1[j]$, but how do we combine the results of several components? Let's assume we have only two components. Then we may take $0$ bad pairs from one component and $j$ from the other, or $1$ from one and $j-1$ from the other, etc., therefore:↵

 $\displaystyle f(j) = b_1[0] \cdot b_2[j] + b_1[1]\cdot b_2[j-1] + b_1[2]\cdot b_2[j-2] + ... + b_1[j]\cdot b_2[0] = \sum_{i=0}^j b_1[i] \cdot b_2[j-i] $. ↵

This is called a *convolution*, and is the same formula for when we multiply two polynomials of the form $p(x) = B_0x^0 + B_1x^1 + B_2x^2 + ... + B_nx^n$ and wishing to find the $j$-th coefficient. The case with more components is similar, except we must convolve all the $b_i$ arrays, i.e. find the product of several polynomials of different lengths.↵

[One can visualize this by treating $x^k$ as a formal symbol meaning "ways to make $k$ bad pairs", which naturally add together when their coefficients are multiplied]↵

However, computing convolutions for two polynomials of size $n$ and $m$ naively is $O(nm)$, which is too slow. There is just the right tool in the AtCoder library for that; the function `convolution`. This function uses the Fast Fourier Transform method to convolve two polynomials in $O((n+m) \log (n+m))$. However note that it only accepts two arguments, and convolving multiple polynomials could be inefficient if called in the wrong order; imagine if you had about $N/2$ polynomials, and all of them are short except for one. Then imagine if you convolved a short one to the longest one over and over; suddenly your submission takes $O(N^2 \log N)$ and times out.↵

Fortunately there is a simple fix: just put all the polynomials into a heap / priority queue that sorts them from shortest to longest. While there is more than one polynomial in that heap, withdraw two, convolve them, then put the result back in the heap. ↵

This works in $O(N \log ^2 N)$, the worst case being $N$ polynomials of degree $1$ (length $2$), which would take $O(\log N)$ "cycles" to fully combine, with the convolutions taking a time of $O(N \log N)$ each cycle.↵

And thus finally, you are able to obtain $f(k)$ by reading the coefficients of the final polynomial, and can apply the inclusion-exclusion formula above to obtain the answer.↵
</spoiler>↵

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
en49 Английский Spheniscine 2023-04-10 07:37:52 72 Tiny change: ') = \sum _ S (-1) ^ {k' -> ') = \sum _{k=0} ^N (-1) ^ {k'
en48 Английский Spheniscine 2023-04-10 06:13:28 2 Tiny change: 'airs(N-k)$, note we d' -> 'airs(N-k)$; note we d'
en47 Английский Spheniscine 2023-04-10 06:11:08 31
en46 Английский Spheniscine 2023-04-10 06:10:41 64
en45 Английский Spheniscine 2023-04-10 06:08:01 354
en44 Английский Spheniscine 2023-04-10 06:06:06 2148 Necro-edit to problem F with a new explanation of PIE. Hopefully this is easier to understand than the [previous revision](https://codeforces.me/topic/83606/en43).
en43 Английский Spheniscine 2020-10-01 17:22:45 2
en42 Английский Spheniscine 2020-10-01 17:15:54 224
en41 Английский Spheniscine 2020-10-01 06:33:51 4
en40 Английский Spheniscine 2020-09-28 20:14:07 354
en39 Английский Spheniscine 2020-09-28 08:55:27 18
en38 Английский Spheniscine 2020-09-28 07:17:42 127
en37 Английский Spheniscine 2020-09-28 03:49:49 6
en36 Английский Spheniscine 2020-09-28 03:07:03 212
en35 Английский Spheniscine 2020-09-27 16:46:37 92
en34 Английский Spheniscine 2020-09-27 16:43:49 8
en33 Английский Spheniscine 2020-09-27 16:42:02 26
en32 Английский Spheniscine 2020-09-27 16:38:14 162
en31 Английский Spheniscine 2020-09-27 16:33:24 403
en30 Английский Spheniscine 2020-09-27 14:56:03 26
en29 Английский Spheniscine 2020-09-27 13:45:55 3
en28 Английский Spheniscine 2020-09-27 12:59:30 4
en27 Английский Spheniscine 2020-09-27 12:56:43 43
en26 Английский Spheniscine 2020-09-27 12:49:54 54
en25 Английский Spheniscine 2020-09-27 12:48:55 706 (published)
en24 Английский Spheniscine 2020-09-27 12:22:19 2414
en23 Английский Spheniscine 2020-09-27 11:59:41 106
en22 Английский Spheniscine 2020-09-27 11:56:26 160
en21 Английский Spheniscine 2020-09-27 11:51:24 8
en20 Английский Spheniscine 2020-09-27 11:49:37 83
en19 Английский Spheniscine 2020-09-27 11:46:48 461
en18 Английский Spheniscine 2020-09-27 11:38:00 1233
en17 Английский Spheniscine 2020-09-27 11:11:24 81
en16 Английский Spheniscine 2020-09-27 11:08:04 22
en15 Английский Spheniscine 2020-09-27 10:52:14 10
en14 Английский Spheniscine 2020-09-27 10:51:32 8
en13 Английский Spheniscine 2020-09-27 10:49:12 1
en12 Английский Spheniscine 2020-09-27 10:44:18 9
en11 Английский Spheniscine 2020-09-27 10:37:55 4967
en10 Английский Spheniscine 2020-09-27 09:21:45 530 Tiny change: 'L_bad(1)}|\n</spoile' -> 'L_bad(1)}|$\n</spoile'
en9 Английский Spheniscine 2020-09-27 08:47:07 587
en8 Английский Spheniscine 2020-09-27 08:31:38 7 Tiny change: 'n$ pairs (either good or ba' -> 'n$ pairs (good or ba'
en7 Английский Spheniscine 2020-09-27 08:07:31 5
en6 Английский Spheniscine 2020-09-27 08:06:35 660
en5 Английский Spheniscine 2020-09-27 07:50:06 780 Tiny change: ' way:\n\n$a = x+y\\\n= x+y+z$\n</spoil' -> ' way:\n\n$$a = x+y\\\n= x+y+z$$\n</spoil'
en4 Английский Spheniscine 2020-09-27 07:33:17 63
en3 Английский Spheniscine 2020-09-27 07:31:53 1068
en2 Английский Spheniscine 2020-09-27 07:30:59 1262 Tiny change: 'e AtCoder implement' -> 'e AtCoder DSU implement'
en1 Английский Spheniscine 2020-09-27 07:17:19 4912 Initial revision (saved to drafts)