<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>XianRui&apos;s Blog</title><description>Dispel the gloom and restore clear skies once more!</description><link>https://blog.517group.cn/</link><language>en</language><item><title>How to Calculate the General Term Formula</title><link>https://blog.517group.cn/posts/20260610200322/</link><guid isPermaLink="true">https://blog.517group.cn/posts/20260610200322/</guid><description>The idea behind transforming a linear recursive formula into a general term formula</description><pubDate>Tue, 16 Jun 2026 13:51:22 GMT</pubDate><content:encoded>&lt;p&gt;This passage is used to introduce how to calculate general term formula by linear recursive formula. Let&apos;s know some basic information before we start.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Linear&lt;/strong&gt;: Each term is a linear term of $a$. For example it doesn&apos;t exist: $a_i \times a_j$, ${a_i}^2$.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Homogeneous&lt;/strong&gt;: There is no extra $f(n)$ at end. Warning: constant function ($f(n)$ is always the constant) also be included.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Constant Coefficients&lt;/strong&gt;: All coefficients of $a$ is constant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Order $k$&lt;/strong&gt; : The current item depends on at most the first k items before it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then you can use these tags to check which kind of recursive formula fit this article.&lt;/p&gt;
&lt;h1&gt;Linear Homogeneous Recurrence with Constant Coefficients&lt;/h1&gt;
&lt;p&gt;This is the most easy one. This kind of recurrence can be express by below formula:&lt;/p&gt;
&lt;p&gt;$$
a_n = c_1a_{n-1} + c_2a_{n-2} + \cdots + c_ka_{n-k}
$$&lt;/p&gt;
&lt;p&gt;For this kind of recurrence, we can use a general method called characteristic equation to solve it. The Core idea of characteristic equation is: An exponential sequence $r^n$ only changes by a constant factor when shifted.&lt;/p&gt;
&lt;p&gt;So we can let $a_n = r^n$, give you a &lt;a href=&quot;https://www.luogu.com.cn/problem/P11735&quot;&gt;example&lt;/a&gt;:
$$
25 a_n + 20 a_{n-1} = 12 a_{n-2}
$$&lt;/p&gt;
&lt;p&gt;Then we have:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
25 r^n + 20 r^{n-1} &amp;amp;= 12 r^{n-2} \
25 r^2 + 20 r &amp;amp;= 12 \
25 r^2 + 20 r - 12 &amp;amp;= 0
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;This is characteristic equation, then we can calculate $r$ by it. And we should have some little categorical discussion on it.&lt;/p&gt;
&lt;h2&gt;Different Root&lt;/h2&gt;
&lt;p&gt;Define the solution set is $r_1, \dots ,r_k$, then the recurrence can be expressed by&lt;/p&gt;
&lt;p&gt;$$
a_n = \sum_{i=1}^k A_i {r_i}^n
$$&lt;/p&gt;
&lt;p&gt;Then if problem give we the first $k$ element, we can calculate the coefficients $A$ by them.&lt;/p&gt;
&lt;p&gt;As the example I give, the root of $25 r^2 + 20r -12 = 0$ is $r_1 = \frac{2}{5}, r_2 = -\frac{6}{5}$, so we can write the general terms as&lt;/p&gt;
&lt;p&gt;$$
r_n = A (\frac{2}{5})^n + B (-\frac{6}{5})^n
$$&lt;/p&gt;
&lt;p&gt;Then if we have any two element of this sequence we can calculate $A, B$, but in &lt;a href=&quot;https://www.luogu.com.cn/problem/P11735&quot;&gt;this problem&lt;/a&gt;, question setter give u only $a_0$. What to do? hold on. The setter give us a special condition: $\forall i \ge 0$ satisfy $a_i \ge 0$.&lt;/p&gt;
&lt;p&gt;The second term is a negative number less than $-1$, so as $n$ increase, the absolute value of it will increase and it&apos;s positive and negative alternately. But the first term is decrease as $n$ increase. So it $B \not = 0$, we always exist $a_i &amp;lt; 0$.&lt;/p&gt;
&lt;p&gt;So the answer is obvious. $A = a_0, B = 0$, the general term of $a$ is
$$
a_n = a_0 \times \left(\frac{2}{5}\right)^n
$$&lt;/p&gt;
&lt;h2&gt;Repeat Root&lt;/h2&gt;
&lt;p&gt;If the characteristic equation is
$$
\sum_{i=1}^k (r-r_i)^{m_i} = 0
$$&lt;/p&gt;
&lt;p&gt;We can express general term as&lt;/p&gt;
&lt;p&gt;$$
a_n = \sum_{i=1}^k \left[\left(\sum_{j=0}^{m_i}C_{i,j}n^j\right){r_i}^n\right]
$$&lt;/p&gt;
&lt;p&gt;OK, I know it is hard to understand such a ugly expression, so let&apos;s see a example.&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;amp; a = {1, 6, 20, \dots } \qquad \text{index start from 0}\
&amp;amp; a_n = 4 a_{n-1} - 4 a_{n-2} \
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;Let $a_n = r^n$, then we can have below characteristic equation&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
r^n &amp;amp;= 4 r^{n-1} - 4 r^{n-2} \
r^2 &amp;amp;= 4r - 4\
r^2 - 4r + 4 &amp;amp;= 0 \
(r - 2)^2 &amp;amp;= 0
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;Ok know we have repeat root. Base on formula above, we have below general term.&lt;/p&gt;
&lt;p&gt;$$
a_n = (A + Bn) 2^n
$$&lt;/p&gt;
&lt;p&gt;Then We can use the first two terms to solve for A and B&lt;/p&gt;
&lt;p&gt;$$
\begin{cases}
1 = (A + B \times 0) \times 2^0 \
6 = (A + B \times 1) \times 2^1
\end{cases}
\qquad
\begin{cases}
A = 1\
B = 2
\end{cases}
$$&lt;/p&gt;
&lt;p&gt;So the final general term of this sequence is&lt;/p&gt;
&lt;p&gt;$$
a_n = (1 + 2n) \times 2^n
$$&lt;/p&gt;
&lt;p&gt;You can verify it using enumeration.&lt;/p&gt;
&lt;h1&gt;Linear non-Homogeneous Recurrence with Constant Coefficients&lt;/h1&gt;
&lt;p&gt;But we know, in lot of stuation, the recurrence always non-homogeneous. So should we give up? No! This part let we know how to solve general term in linear non-homogeneous recurrence with constant coefficients.&lt;/p&gt;
&lt;p&gt;Also we start with a little example.&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
a_0 &amp;amp;= 1 \
a_n &amp;amp;= 2 a_{n-1} + 3
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;We should find a sequence $p$ which also satisfy this recurrence. In this case, we can find a sequence $p = {-3,-3,-3,\cdots}$. The core idea is to minus the non-homogeneous part of origin recurrence.&lt;/p&gt;
&lt;p&gt;So now we have two sequence: $a_n = 2a_{n-1}+3, p_n = 2p_{n-1}+3$, if we let the recurrence of $a$ minus $p$, we will take a sequence $d$, and this sequence is a linear homogeneous recurrence with constant coefficients.&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
d_n
&amp;amp;= a_n - p_n \
&amp;amp;= (2a_{n-1} + 3) - (2 p_{n-1} + 3)\
&amp;amp;= 2(a_{n-1} - p_{n-1})\
&amp;amp;= 2d_{n-1}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;Then use common sense or the method I introduce last part, we can know $d_n = A 2^{n}$, substitute $d_n$ and $p_n$ we will get general term of $a_n$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
a_n = p_n + d_n\
a_n = -3 + A2^n
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;Then we can use the start value of $a$ to calculate value of $A$, it&apos;s $8$.&lt;/p&gt;
&lt;h1&gt;Summarize&lt;/h1&gt;
&lt;p&gt;This article introduce some common and easy situation when we are translating recurrence to general term. In most of situation, Matrix Fast Exponentiation also OKay. But math can optmize $O(k^3 \log n)$ to $O(1)$&lt;/p&gt;
</content:encoded></item><item><title>Solution Report of Balance Tree(Medium) Topic</title><link>https://blog.517group.cn/posts/202606071324/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202606071324/</guid><description>Give them a real war!</description><pubDate>Wed, 10 Jun 2026 19:57:25 GMT</pubDate><content:encoded>&lt;h1&gt;P1552 [APIO2012] 派遣&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P1552&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;s&gt;Reference Blog&lt;/s&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The problem gives we a directed and rooted tree. So we can use DSU on tree to solve this problem.&lt;/p&gt;
&lt;p&gt;For each leaf, initialize a DS with this node. When combine information of son to take more ninja in limit, pop the most expensive ninja and leader is now node, so answer is &lt;code&gt;set.size() * leader[u]&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;P2173 [ZJOI2012] 网络&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P2173&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/vknsmpgp&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We noticed that the color in this problem less than 10 type. So we can maintain information of each chain which different color. Actually, it&apos;s a common trick to maintain chain.&lt;/p&gt;
&lt;p&gt;When connect node $u$ and $v$, we should check they must on different chain, and because of nature of chain, $u,v$ must be the endpoints of their chain. This part we can write a function &lt;code&gt;link&lt;/code&gt; to quick check it&apos;s valid to connect these node or not.&lt;/p&gt;
&lt;p&gt;For operator &lt;code&gt;1&lt;/code&gt;, we can regard it as cut and link, so the &lt;code&gt;link&lt;/code&gt; function in last part can be used here. Cut operator is easy, FHQ-Treap can do split operator easily.&lt;/p&gt;
&lt;h1&gt;P2497 [SDOI2012] 基站建设&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P2497&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/hnmu6zk2&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let $f_i$ denotes the minimum cost make node $i$ connect to starter.&lt;/p&gt;
&lt;p&gt;We can get a transition function that $f_i = \min_{j&amp;lt;i}(f_j+w(i,j))$, and function $w(i,j)$ denotes the minimum cost to connect node $i$ and node $j$. We can use Pythagorean theorem to expand it.&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\because\ \text{Pythagorean\ theorem,} &amp;amp; \ (r&apos;_i - r_j)^2 + (x_i - x_j)^2 = (r&apos;_i + r_j)^2 \
\therefore\ r&apos;_i = &amp;amp; \frac{(x_i-x_j)^2}{4r_j} \
\
w(i,j)
&amp;amp;= \sqrt{r&apos;_i} + v_i \
&amp;amp;= \sqrt{\frac{(x_i-x_j)^2}{4r_j}} + v_i\
&amp;amp;= \frac{x_i-x_j}{2\sqrt{r_j}}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.luogu.com.cn/upload/image_hosting/ckbbfiuz.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then we can change transition function to:
$$
\begin{aligned}
f_i &amp;amp;= f_j + \frac{x_i}{2\sqrt{r_j}} - \frac{x_j}{2\sqrt{r_j}} + v_i\
f_i-v_i &amp;amp;= \left(\frac{1}{2\sqrt{r_j}}\right)x_i + \left(f_j - \frac{x_j}{2\sqrt{r_j}}\right)
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;Li-Chao Segment tree can solve this problem easily, just query minimum at $x_i$, transition, and push line $i$.&lt;/p&gt;
&lt;h1&gt;P3081 [USACO13MAR] Hill Walk G&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3081&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/mu50wbzx&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most of solution in luogu is using Lichao Segment Tree. But I want to solve these problem by Balance Tree. So here is a different solution.&lt;/p&gt;
&lt;p&gt;Also we need scanning-line to find all segment which satisfy $x_1 \le x &amp;lt; x_2$ ($x$ is the position bassie located), then we need to find a way to sort these segment let we can use single operator such eraser begin to maintain answer quickly.&lt;/p&gt;
&lt;p&gt;Because problem promises the segment never cross, so the hierarchical relationship will never changed with position of bassie.&lt;/p&gt;
&lt;p&gt;Then we can sort it by now position.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Comparator {
    const std::vector&amp;lt;Segment&amp;gt;* seg;
    const ll* sweep_x;

    Comparator(
        const std::vector&amp;lt;Segment&amp;gt;* seg_ptr = nullptr,
        const ll* x_ptr = nullptr
    ) : seg(seg_ptr), sweep_x(x_ptr) {}

    bool operator()(int lhs, int rhs) const {
        if (lhs == rhs) {
            return false;
        }

        const Segment&amp;amp; a = (*seg)[lhs];
        const Segment&amp;amp; b = (*seg)[rhs];
        const ll x = *sweep_x;

        __int128 dx_a = a.x2 - a.x1;
        __int128 dy_a = a.y2 - a.y1;
        __int128 dx_b = b.x2 - b.x1;
        __int128 dy_b = b.y2 - b.y1;

        __int128 val_a =
            (__int128)a.y1 * dx_a + dy_a * (x - a.x1);

        __int128 val_b =
            (__int128)b.y1 * dx_b + dy_b * (x - b.x1);

        __int128 left = val_a * dx_b;
        __int128 right = val_b * dx_a;

        if (left != right) {
            return left &amp;lt; right;
        }

        return lhs &amp;lt; rhs;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Left part just like monotonic queue, believe it&apos;s easy to realize.&lt;/p&gt;
&lt;h1&gt;P3215 [HNOI2011] 括号修复 / [JSOI2011] 括号序列&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3215&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/tt15pohm&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For this classic problem, I am first time knowing that have a formula to calculate answer. We let &lt;code&gt;(&lt;/code&gt; denotes $-1$, &lt;code&gt;)&lt;/code&gt; denotes $1$, define $\text{premax}$ denotes maximum of prefix sum, define $\text{sufmin}$ denotes minimum of suffix sum. Then we can denotes answer by&lt;/p&gt;
&lt;p&gt;$$
\bigg\lceil\frac{\text{premax}}{2}\bigg\rceil + \left\lceil\frac{|\text{sufmin}|}{2}\right\rceil
$$&lt;/p&gt;
&lt;p&gt;The prove is easy. Don&apos;t misremember the formula, it&apos;s not sum of premax ad sufmin then divide by two, here is a counterexample: &lt;code&gt;)(&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then we can use FHQ-Treap or Segment Tree to solve this problem easily.&lt;/p&gt;
&lt;h1&gt;P3224 [HNOI2012] 永无乡&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3224&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;s&gt;Reference Blog&lt;/s&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Also a classic problem, in this problem we know Balance Tree also can be used to simulate DSU.&lt;/p&gt;
&lt;p&gt;Use Treap to simulate DSU, combine each block and query the k-th maximum is easy.&lt;/p&gt;
&lt;h1&gt;P3268 [JLOI2016] 圆的异或并&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3268&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/d1wbcgxw&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We are given a strange condition just like &lt;a href=&quot;#p3081-usaco13mar-hill-walk-g&quot;&gt;[USACO13MAR] Hill Walk G&lt;/a&gt;, because of the circle in this problem never cross, so the relative position never change. We can use set and scanning-line to calculate nesting levels of a circle.&lt;/p&gt;
&lt;p&gt;Then because of we are calculating XOR area, even nesting levels lead to negative and odd lead to positive area.&lt;/p&gt;
&lt;h1&gt;P3586 [POI 2015 R2] 物流 Logistics&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3586&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;s&gt;Reference Blog&lt;/s&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The core idea is to find the total contribution is
$$
\sum \min(a_i, s)
$$&lt;/p&gt;
&lt;p&gt;We will output &lt;code&gt;TAK&lt;/code&gt; if total contribution greater than $c\times s$, or output &lt;code&gt;NIE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then we can use BIT or Balance Tree solve it easily.&lt;/p&gt;
&lt;h1&gt;P3968 [TJOI2014] 电源插排&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3968&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/tbjdg0vp&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;First we can simulate the operation of this problem, to get all possible position. Because full scope can reach $10^9$, but the query just less than $10^5$, so this simulte can help we discretize.&lt;/p&gt;
&lt;p&gt;So the first challenge is to simulate it. We can build two set. And one for coming, another one for leaving. More details, let the set used to manage coming to sort by length and right endpoint. So the begin is the range which next student will use.&lt;/p&gt;
&lt;p&gt;And when a student leave, we need combine three ranges: &lt;code&gt;[l, x)&lt;/code&gt;, &lt;code&gt;[x]&lt;/code&gt;, &lt;code&gt;(x,r]&lt;/code&gt;. We let set used to manage leaving sort by left point, then we can use binary search ( &lt;code&gt;find()&lt;/code&gt;, the member function of &lt;code&gt;std::multiset&lt;/code&gt;) can solve the question easily.&lt;/p&gt;
&lt;p&gt;Whatmore, there is a small detail when manage leaving. We can allow empty range exist. For example, there is a range &lt;code&gt;[x, x]&lt;/code&gt;, then a student use it, we also can make three ranges: &lt;code&gt;[x, x-1]&lt;/code&gt;, &lt;code&gt;[x,x]&lt;/code&gt;, &lt;code&gt;[x+1, x]&lt;/code&gt;. Although the first and last range is invaild, but it can minus a lot of unnecessary special check.&lt;/p&gt;
&lt;p&gt;After simulation, count is easy. This approach is off-line, and count we can use BIT or Segment Tree to solve it easily.&lt;/p&gt;
</content:encoded></item><item><title>Guide of Traditional Flower Game</title><link>https://blog.517group.cn/posts/traditionalFlowerGuide/</link><guid isPermaLink="true">https://blog.517group.cn/posts/traditionalFlowerGuide/</guid><description>Welcome to our game-site!</description><pubDate>Sat, 30 May 2026 14:01:01 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Links:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://game.517group.cn&quot;&gt;Game Portal&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://space.bilibili.com/3546980278077664&quot;&gt;Bilibili Space&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;OK, I am the second author of this game, &lt;strong&gt;XianRuiDendro&lt;/strong&gt;. As you can see, this article is here to introduce our game to you, and maybe also to prove that our design team was awake for at least part of the development process.&lt;/p&gt;
&lt;h1&gt;Sign Up / In / Out&lt;/h1&gt;
&lt;p&gt;Everything begins with signing up.&lt;/p&gt;
&lt;p&gt;For every new account, we prepare one starter character from the permanent pool. These characters are all carefully designed, interesting, and absolutely not suspiciously strong in the hands of your friend who says, “I barely know how to play.”&lt;/p&gt;
&lt;p&gt;Signing in is simple. The username and password rules are friendly enough that you will not need a law degree to create an account.&lt;/p&gt;
&lt;p&gt;You can find the sign-out button on the left side of the page. We put it there because sometimes people need to leave, even from greatness.&lt;/p&gt;
&lt;h1&gt;Home Page&lt;/h1&gt;
&lt;p&gt;After signing in, you will arrive at the home page. There are three portals:&lt;/p&gt;
&lt;p&gt;The left portal is &lt;strong&gt;Local War&lt;/strong&gt;, where you can play with a friend offline. Please do not cut the internet connection, though. The game is powerful, but not magic.&lt;/p&gt;
&lt;p&gt;The middle portal is &lt;strong&gt;Online War&lt;/strong&gt;. Your wins and losses affect your in-game rank, and your seasonal rewards depend on that rank. This part will be introduced later, because rewards deserve dramatic timing.&lt;/p&gt;
&lt;p&gt;The right portal is &lt;strong&gt;Computer War&lt;/strong&gt;, where you can fight bosses. They look serious. You will probably be fine. Probably.&lt;/p&gt;
&lt;p&gt;There is also a header.&lt;/p&gt;
&lt;p&gt;The leftmost logo is the name of our game. Click it to return to the home page. The other buttons, from left to right, lead to Bag, Reward, Character, Pull, Shop, Announcement, Other, and Settings.&lt;/p&gt;
&lt;h1&gt;Character Page&lt;/h1&gt;
&lt;p&gt;Most functions on this page are easy to discover by yourself, which is good, because we believe players should enjoy both combat and mild investigation.&lt;/p&gt;
&lt;p&gt;One small but useful feature: you can click the name of a character you own. A window will appear and show detailed information, including how much damage this character can deal with each skill. This is especially useful when you want to explain that your loss was caused by “experimental data collection.”&lt;/p&gt;
&lt;h1&gt;Fight Mechanism&lt;/h1&gt;
&lt;p&gt;Here comes the second most important part of the game.&lt;/p&gt;
&lt;h2&gt;Operation System&lt;/h2&gt;
&lt;p&gt;In online battles, both players use the same control scheme:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;W / A / D&lt;/code&gt;: move and jump&lt;/li&gt;
&lt;li&gt;&lt;code&gt;S&lt;/code&gt;: character skill&lt;/li&gt;
&lt;li&gt;&lt;code&gt;K&lt;/code&gt;: weapon skill&lt;/li&gt;
&lt;li&gt;&lt;code&gt;J&lt;/code&gt;: dash&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Y&lt;/code&gt;: surrender, for players with great emotional intelligence&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In local battles, P1 uses &lt;code&gt;W / A / D&lt;/code&gt;, &lt;code&gt;S&lt;/code&gt;, &lt;code&gt;K&lt;/code&gt;, &lt;code&gt;J&lt;/code&gt;, and &lt;code&gt;Y&lt;/code&gt;.&lt;br /&gt;
P2 uses the arrow keys, Numpad &lt;code&gt;2&lt;/code&gt; for weapon skill, Numpad &lt;code&gt;1&lt;/code&gt; for dash, and &lt;code&gt;P&lt;/code&gt; to surrender.&lt;/p&gt;
&lt;p&gt;In Boss mode, Numpad &lt;code&gt;9&lt;/code&gt; or number &lt;code&gt;9&lt;/code&gt; can make the Boss surrender. This is not cowardice. This is advanced negotiation.&lt;/p&gt;
&lt;p&gt;Every battle has HP, weapon cooldown, character skill cooldown, dash charges, and status effects. Dash charges recover over time, so do not spend all of them just to look stylish. Unless it works. Then it was strategy.&lt;/p&gt;
&lt;h2&gt;Negative Status&lt;/h2&gt;
&lt;p&gt;The game currently has several important negative statuses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Poison&lt;/strong&gt;: caused by weapons like Flying Sand Talisman. It lasts 10 seconds, ticks every 2 seconds, and can stack up to 5 layers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stun&lt;/strong&gt;: briefly stops your action. Heavy attacks and some skills can cause this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bind&lt;/strong&gt;: usually related to web zones. Leaving the zone can add bind stacks, slowing movement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jump Lock&lt;/strong&gt;: when active, jumping is forbidden. Very rude, very effective.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vulnerable&lt;/strong&gt;: makes you take extra damage. This is the game politely saying, “Please reconsider standing there.”&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Gacha Mechanism&lt;/h1&gt;
&lt;p&gt;The gacha system has two kinds of pools: the &lt;strong&gt;Permanent Pool&lt;/strong&gt; and the &lt;strong&gt;Limited Pool&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The Permanent Pool contains the starter characters: &lt;strong&gt;Qing Lan Walker&lt;/strong&gt;, &lt;strong&gt;Chi Yan Fighter&lt;/strong&gt;, &lt;strong&gt;Ling Mu Watcher&lt;/strong&gt;, and &lt;strong&gt;Ant Summoner&lt;/strong&gt;. These characters are always treated as permanent characters. They are stable, friendly, and still fully capable of making your opponent question the meaning of balance.&lt;/p&gt;
&lt;p&gt;The Limited Pool contains characters outside the permanent list, such as &lt;strong&gt;Shadow Ninja&lt;/strong&gt; and &lt;strong&gt;Burning Blade&lt;/strong&gt;. Limited characters appear as the current UP target when their banner is active.&lt;/p&gt;
&lt;p&gt;A single pull costs &lt;strong&gt;1 Great Wenhua&lt;/strong&gt;.&lt;br /&gt;
A ten-pull costs &lt;strong&gt;10 Great Wenhua&lt;/strong&gt;.&lt;br /&gt;
Free ten-pulls do not advance pity counters, because free things are allowed to be nice, but not that nice.&lt;/p&gt;
&lt;p&gt;For the Permanent Pool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Base character rate: &lt;strong&gt;0.6%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;80th pull character rate: &lt;strong&gt;3%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;From the 81st pull onward, the rate increases&lt;/li&gt;
&lt;li&gt;120th pull: guaranteed character&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the Limited Pool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Total character rate starts at &lt;strong&gt;0.75%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;On a character result, there is a &lt;strong&gt;50%&lt;/strong&gt; chance to get the current UP character&lt;/li&gt;
&lt;li&gt;There is also a &lt;strong&gt;50%&lt;/strong&gt; chance to lose the roll and get a random permanent character&lt;/li&gt;
&lt;li&gt;So the starting rate is effectively &lt;strong&gt;0.375% UP&lt;/strong&gt; and &lt;strong&gt;0.375% permanent spook&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;80th pull total character rate: &lt;strong&gt;3.75%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Due to the limited multiplier, the total character rate reaches 100% before the normal 120-pull ceiling&lt;/li&gt;
&lt;li&gt;If you lose the 50/50 and get a permanent character, the limited guarantee activates&lt;/li&gt;
&lt;li&gt;After that, you will get the current UP character within at most &lt;strong&gt;60 paid pulls&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In simple player language: if the Limited Pool smiles, you get the UP. If it smiles in a legally different way, you get a permanent character and then the game owes you the UP within 60 pulls. Please remember this sentence when probability starts wearing a fake moustache.&lt;/p&gt;
&lt;p&gt;If a paid pull does not produce a character, you receive &lt;strong&gt;1 Residue&lt;/strong&gt;. Reaching 20 paid pulls without a character grants &lt;strong&gt;1 free ten-pull&lt;/strong&gt;. Reaching 60 paid pulls without a character grants a &lt;strong&gt;Wenhua Voucher&lt;/strong&gt;, which is converted into &lt;strong&gt;10 Great Wenhua&lt;/strong&gt; when the banner refreshes.&lt;/p&gt;
&lt;p&gt;Duplicate characters work like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First copy: unlocks the character&lt;/li&gt;
&lt;li&gt;Next copies: become constellation materials, up to 6&lt;/li&gt;
&lt;li&gt;After full constellation: extra copies become &lt;strong&gt;5 Wenhua&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So even duplicates have value. This is important for morale, especially after the gacha system says “congratulations” in a very suspicious tone.&lt;/p&gt;
&lt;h1&gt;Paid Mechanism&lt;/h1&gt;
&lt;p&gt;Now comes the most important part.&lt;/p&gt;
&lt;p&gt;The shop sells Wenhua Prototypes. Each top-up tier has its own first-purchase bonus, and every tier calculates the first bonus separately. This means you can enjoy the sacred experience of saying, “Actually, this is very cost-effective.”&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Normal Reward&lt;/th&gt;
&lt;th&gt;First Top-Up Reward&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;6 CNY&lt;/td&gt;
&lt;td&gt;60 Wenhua Prototypes&lt;/td&gt;
&lt;td&gt;60 * 2 Wenhua Prototypes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30 CNY&lt;/td&gt;
&lt;td&gt;300 Wenhua Prototypes&lt;/td&gt;
&lt;td&gt;300 * 2 Wenhua Prototypes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;98 CNY&lt;/td&gt;
&lt;td&gt;980 Wenhua Prototypes&lt;/td&gt;
&lt;td&gt;980 * 3 Wenhua Prototypes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;198 CNY&lt;/td&gt;
&lt;td&gt;1980 * 2 Wenhua Prototypes&lt;/td&gt;
&lt;td&gt;1980 * 6 Wenhua Prototypes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;328 CNY&lt;/td&gt;
&lt;td&gt;3280 * 3 Wenhua Prototypes&lt;/td&gt;
&lt;td&gt;3280 * 10 Wenhua Prototypes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;648 CNY&lt;/td&gt;
&lt;td&gt;6480 * 10 Wenhua Prototypes&lt;/td&gt;
&lt;td&gt;6480 * 40 Wenhua Prototypes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The 648 CNY first top-up gives &lt;strong&gt;259,200&lt;/strong&gt; Wenhua Prototypes (For 40 times of common games). At this point, the number is so large that the calculator also needs a moment.&lt;/p&gt;
&lt;p&gt;Wenhua Prototypes can be converted into Wenhua Fragments, and Wenhua Fragments can be converted into Great Wenhua for pulling characters. So the paid system is simple: recharge, convert, pull, win, and then claim it was all skill.&lt;/p&gt;
&lt;h1&gt;Final Advertisement&lt;/h1&gt;
&lt;p&gt;Thank you for reading this introduction from &lt;strong&gt;XianRuiDendro&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you are interested in the game, want to follow updates, ask questions, find opponents, or simply watch everyone argue about balance with great confidence, please join our official QQ group:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1106119045&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;See you in the game.&lt;/p&gt;
</content:encoded></item><item><title>Solution Report of Balance Tree(Easy) Topic</title><link>https://blog.517group.cn/posts/202605202114/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202605202114/</guid><description>Balanced Tree problem list, but few Balanced Tree solutions.</description><pubDate>Wed, 20 May 2026 21:14:25 GMT</pubDate><content:encoded>&lt;h1&gt;P1110 [ZJOI2007] 报表统计&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P1110&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;s&gt;Reference Blog&lt;/s&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Easy problem, we should maintain a 2D-vector with $n$ row, then answer the question from problem.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;Min_Gap&lt;/code&gt;, using a &lt;code&gt;std::set&lt;/code&gt;(or Balance Tree) just will have 3 changes when we insert a number: delete old info from old tail and next head, then insert new info from old tail and new element, new info from new element and next head.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;Min_Sort_Gap&lt;/code&gt;, using another &lt;code&gt;std::set&lt;/code&gt;(or Balance Tree), just query the previous and the next element then find min gap global.&lt;/p&gt;
&lt;h1&gt;P1486 [NOI2004] 郁闷的出纳员&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P1486&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;s&gt;Reference Blog&lt;/s&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We noticed that the number of operator &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;S&lt;/code&gt; just hundred, so voilence add and minus all node is okay; rest part is common Balance tree.&lt;/p&gt;
&lt;h1&gt;P2869 [USACO07DEC] Gourmet Grazers G&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P2869&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/tpmedkhv&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This a problem using a classic idea: Sort by one variable, then use natrue by another variable to solve problem.&lt;/p&gt;
&lt;p&gt;Same here, we sort all element (include grass) by taste, then push then into a multiset let it sort by price, then we can find the minimum price.&lt;/p&gt;
&lt;h1&gt;P3466 [POI 2008] KLO-Building blocks&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3466&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;s&gt;Reference Blog&lt;/s&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is not hard to find we can traverse all $k$-length range and calculate the answer.&lt;/p&gt;
&lt;p&gt;Then the best answer is change all element into median. Then we need a data structure which can find median, calculate the sum of all element less (or greater) than a value. This function can use Balance Tree to solve it easily.&lt;/p&gt;
&lt;h1&gt;P7619 [COCI 2011/2012 #2] RASPORED&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P7619&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/kca1drz5&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we fire this pancake with order sequence $p$, answer can be express by:
$$
\begin{aligned}
Answer
&amp;amp;= \sum_{i=1}^n\left(L_{p_i}-\sum_{j=1}^iT_{p_j}\right) \
&amp;amp;= \sum_{i=1}^nL_{p_i} - \sum_{i=1}^n(n-i+1)T_{p_i}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;Then sum of $L$ can calculate at the beginning, right part can sort $T$ in ascending order. Balance Tree can solve it easily.&lt;/p&gt;
&lt;h1&gt;P11373 「CZOI-R2」天平&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P11373&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/s827aknc&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here&apos;s another trick: When we need to check if there exists a sequence $c$ satisfying $\forall c_i\lt\Z$ such that $\sum a_ic_i = v$, and then simply check if $v$ is a multiple of $\gcd(c_i)$.&lt;br /&gt;
This is because of Bézù Theorem.&lt;/p&gt;
&lt;p&gt;Then it&apos;s easy to find that $\gcd(a_i) = \gcd[\gcd(a_1, a_2), \gcd(a_2, a_3), \dots]$ (It&apos;s same with prove $\gcd(a,b,c) = \gcd(\gcd(a,b), \gcd(b,c))$, easy to prove by emotional understanding).&lt;/p&gt;
&lt;p&gt;Use Subtractive Euclidean Algorithm, that is $\gcd(a, b) = \gcd(a, a-b)$, can be deduced:
$$
\begin{aligned}
\gcd(a_1, a_2, \dots, a_n) &amp;amp;= \gcd[\gcd(a_1, a_2), \gcd(a_2, a_3), \dots, \gcd(a_{n-1}, a_n)]\
&amp;amp;= \gcd[\gcd(a_1, a_2-a_1), \gcd(a_2, a_3-a_2), \dots, \gcd(a_{n-1}, a_n-a_{n-1})]\
&amp;amp;= \gcd(a_1, \gcd(b_2, b_3, \dots, b_n))
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;$b_i = a_i - a_{i-1}$ in above equation.&lt;/p&gt;
&lt;p&gt;Then we can use Balance Tree to solve this problem.&lt;/p&gt;
&lt;p&gt;Code is difficult.&lt;/p&gt;
&lt;h1&gt;P12179 DerrickLo&apos;s Game&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P12179&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/8ng6msb3&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Also a tricky problem, the core idea is: because there is no minus operator, the final sequence will be all maximum of origin sequence. So do second operator with all 2-length subsequence is best. Specially, for element $x-1, x-2, x-3$ will use first operator.&lt;/p&gt;
&lt;p&gt;Then we can use Segment Tree to solve this problem.&lt;/p&gt;
&lt;h1&gt;P14379 【MX-S9-T2】「LAOI-16」摩天大楼&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P14379&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/k3lletff&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The problem with mex is usually need to think position of number &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If a range satisfy $f(l, r)=0$:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;there is no &lt;code&gt;1&lt;/code&gt; in whole range;&lt;/li&gt;
&lt;li&gt;the start point and end point of range is &lt;code&gt;1&lt;/code&gt;, and range $(l,r)$ have no $2$.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Emm, nerd writer first think range ${1,2,3,3,2,1}$ also can let $f = 0$, but wrong. That&apos;s becase the problem need us find a cut point in range, instead find $\operatorname{mex}$ for whole range.&lt;/p&gt;
&lt;p&gt;Left part is easy, use Segment Tree can do counting easy.&lt;/p&gt;
</content:encoded></item><item><title>Template of FHQ-Treap</title><link>https://blog.517group.cn/posts/202605192119/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202605192119/</guid><description>non-rotate Treap</description><pubDate>Tue, 19 May 2026 21:19:57 GMT</pubDate><content:encoded>&lt;p&gt;written by ChatGPT.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;array&amp;gt;
#include &amp;lt;cstdint&amp;gt;

template &amp;lt;typename Tp&amp;gt;
using OwnPtr = std::unique_ptr&amp;lt;Tp&amp;gt;;
template &amp;lt;typename Tp&amp;gt;
using RefPtr = Tp*;

class FhqTreap {
private:
    struct Node {
        std::array&amp;lt;RefPtr&amp;lt;Node&amp;gt;, 2&amp;gt; son{nullptr, nullptr};
        int cnt = 0, val = 0, sz = 0;
        std::uint32_t pri = 0;
    };

    std::vector&amp;lt;OwnPtr&amp;lt;Node&amp;gt;&amp;gt; pool;

    std::uint32_t seed = 114514;

    auto rnd() -&amp;gt; std::uint32_t {
        seed ^= seed &amp;lt;&amp;lt; 13;
        seed ^= seed &amp;gt;&amp;gt; 17;
        seed ^= seed &amp;lt;&amp;lt; 5;
        return seed;
    }

    auto size(RefPtr&amp;lt;Node&amp;gt; x) -&amp;gt; int {
        return x ? x-&amp;gt;sz : 0;
    }

    void pushUp(RefPtr&amp;lt;Node&amp;gt; x) {
        if (!x) return;
        x-&amp;gt;sz = x-&amp;gt;cnt + size(x-&amp;gt;son[0]) + size(x-&amp;gt;son[1]);
    }

    auto newNode(int v) -&amp;gt; RefPtr&amp;lt;Node&amp;gt; {
        pool.push_back(std::make_unique&amp;lt;Node&amp;gt;());
        auto p = pool.back().get();

        p-&amp;gt;val = v;
        p-&amp;gt;cnt = p-&amp;gt;sz = 1;
        p-&amp;gt;pri = rnd();

        return p;
    }

    /*
        equal == false:
            x: val &amp;lt; v
            y: val &amp;gt;= v

        equal == true:
            x: val &amp;lt;= v
            y: val &amp;gt; v
    */
    void split(
        RefPtr&amp;lt;Node&amp;gt; p,
        int v,
        RefPtr&amp;lt;Node&amp;gt;&amp;amp; x,
        RefPtr&amp;lt;Node&amp;gt;&amp;amp; y,
        bool equal = false
    ) {
        if (!p) {
            x = y = nullptr;
            return;
        }

        if (p-&amp;gt;val &amp;lt; v || (equal &amp;amp;&amp;amp; p-&amp;gt;val == v)) {
            x = p;
            split(p-&amp;gt;son[1], v, x-&amp;gt;son[1], y, equal);
            pushUp(x);
        } else {
            y = p;
            split(p-&amp;gt;son[0], v, x, y-&amp;gt;son[0], equal);
            pushUp(y);
        }
    }

    auto merge(RefPtr&amp;lt;Node&amp;gt; x, RefPtr&amp;lt;Node&amp;gt; y) -&amp;gt; RefPtr&amp;lt;Node&amp;gt; {
        if (!x || !y) {
            return x ? x : y;
        }

        if (x-&amp;gt;pri &amp;lt; y-&amp;gt;pri) {
            x-&amp;gt;son[1] = merge(x-&amp;gt;son[1], y);
            pushUp(x);
            return x;
        } else {
            y-&amp;gt;son[0] = merge(x, y-&amp;gt;son[0]);
            pushUp(y);
            return y;
        }
    }

    auto kth(RefPtr&amp;lt;Node&amp;gt; x, int k) -&amp;gt; RefPtr&amp;lt;Node&amp;gt; {
        while (x) {
            int ls = size(x-&amp;gt;son[0]);

            if (k &amp;lt;= ls) {
                x = x-&amp;gt;son[0];
            } else if (k &amp;lt;= ls + x-&amp;gt;cnt) {
                return x;
            } else {
                k -= ls + x-&amp;gt;cnt;
                x = x-&amp;gt;son[1];
            }
        }

        return nullptr;
    }

    auto rightMost(RefPtr&amp;lt;Node&amp;gt; x) -&amp;gt; RefPtr&amp;lt;Node&amp;gt; {
        if (!x) return nullptr;
        while (x-&amp;gt;son[1]) x = x-&amp;gt;son[1];
        return x;
    }

    auto leftMost(RefPtr&amp;lt;Node&amp;gt; x) -&amp;gt; RefPtr&amp;lt;Node&amp;gt; {
        if (!x) return nullptr;
        while (x-&amp;gt;son[0]) x = x-&amp;gt;son[0];
        return x;
    }

public:
    RefPtr&amp;lt;Node&amp;gt; rt = nullptr;

    FhqTreap() = default;

    ~FhqTreap() {
        clear();
    }

    void clear() {
        rt = nullptr;
        std::vector&amp;lt;OwnPtr&amp;lt;Node&amp;gt;&amp;gt;().swap(pool);
        seed = 114514;
    }

    void insert(int v) {
        RefPtr&amp;lt;Node&amp;gt; a = nullptr;
        RefPtr&amp;lt;Node&amp;gt; b = nullptr;
        RefPtr&amp;lt;Node&amp;gt; c = nullptr;

        split(rt, v, a, b, false); // a &amp;lt; v, b &amp;gt;= v
        split(b, v, c, b, true);   // c == v, b &amp;gt; v

        if (c) {
            c-&amp;gt;cnt++;
            pushUp(c);
        } else {
            c = newNode(v);
        }

        rt = merge(merge(a, c), b);
    }

    bool remove(int v) {
        RefPtr&amp;lt;Node&amp;gt; a = nullptr;
        RefPtr&amp;lt;Node&amp;gt; b = nullptr;
        RefPtr&amp;lt;Node&amp;gt; c = nullptr;

        split(rt, v, a, b, false); // a &amp;lt; v, b &amp;gt;= v
        split(b, v, c, b, true);   // c == v, b &amp;gt; v

        if (!c) {
            rt = merge(a, b);
            return false;
        }

        if (c-&amp;gt;cnt &amp;gt; 1) {
            c-&amp;gt;cnt--;
            pushUp(c);
        } else {
            auto del = c;
            c = merge(c-&amp;gt;son[0], c-&amp;gt;son[1]);

            del-&amp;gt;son[0] = del-&amp;gt;son[1] = nullptr;
            del-&amp;gt;cnt = del-&amp;gt;sz = 0;
        }

        rt = merge(merge(a, c), b);
        return true;
    }

    int find_rank(int v) {
        RefPtr&amp;lt;Node&amp;gt; a = nullptr;
        RefPtr&amp;lt;Node&amp;gt; b = nullptr;

        split(rt, v, a, b, false); // a &amp;lt; v, b &amp;gt;= v

        int ans = size(a) + 1;

        rt = merge(a, b);
        return ans;
    }

    int find_kth(int k) {
        if (!rt || k &amp;lt;= 0 || k &amp;gt; rt-&amp;gt;sz) return -1;

        auto x = kth(rt, k);
        return x ? x-&amp;gt;val : -1;
    }

    int find_prev(int v) {
        RefPtr&amp;lt;Node&amp;gt; a = nullptr;
        RefPtr&amp;lt;Node&amp;gt; b = nullptr;

        split(rt, v, a, b, false); // a &amp;lt; v, b &amp;gt;= v

        auto x = rightMost(a);
        int ans = x ? x-&amp;gt;val : -1;

        rt = merge(a, b);
        return ans;
    }

    int find_next(int v) {
        RefPtr&amp;lt;Node&amp;gt; a = nullptr;
        RefPtr&amp;lt;Node&amp;gt; b = nullptr;

        split(rt, v, a, b, true); // a &amp;lt;= v, b &amp;gt; v

        auto x = leftMost(b);
        int ans = x ? x-&amp;gt;val : -1;

        rt = merge(a, b);
        return ans;
    }
};

auto main() -&amp;gt; int {
    int T;
    std::cin &amp;gt;&amp;gt; T;

    FhqTreap treap;

    while (T--) {
        int op, x;
        std::cin &amp;gt;&amp;gt; op &amp;gt;&amp;gt; x;

        switch (op) {
            case 1:
                treap.insert(x);
                break;
            case 2:
                treap.remove(x);
                break;
            case 3:
                std::cout &amp;lt;&amp;lt; treap.find_rank(x) &amp;lt;&amp;lt; &apos;\n&apos;;
                break;
            case 4:
                std::cout &amp;lt;&amp;lt; treap.find_kth(x) &amp;lt;&amp;lt; &apos;\n&apos;;
                break;
            case 5:
                std::cout &amp;lt;&amp;lt; treap.find_prev(x) &amp;lt;&amp;lt; &apos;\n&apos;;
                break;
            case 6:
                std::cout &amp;lt;&amp;lt; treap.find_next(x) &amp;lt;&amp;lt; &apos;\n&apos;;
                break;
        }
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Template of Splay Tree</title><link>https://blog.517group.cn/posts/202605092020/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202605092020/</guid><description>Security Template</description><pubDate>Sat, 09 May 2026 20:20:57 GMT</pubDate><content:encoded>&lt;p&gt;Security template of Splay Tree is coming~&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;array&amp;gt;
#include &amp;lt;cassert&amp;gt;

template &amp;lt;typename Tp&amp;gt;
using OwnPtr = std::unique_ptr&amp;lt;Tp&amp;gt;;
template &amp;lt;typename Tp&amp;gt;
using RefPtr = Tp*;

class Splay {
private:
    struct Node {
        std::array&amp;lt;RefPtr&amp;lt;Node&amp;gt;, 2&amp;gt; son{nullptr, nullptr};
        RefPtr&amp;lt;Node&amp;gt; fat = nullptr;
        int cnt = 0, val = 0, sz = 0;
    };
    std::vector&amp;lt;OwnPtr&amp;lt;Node&amp;gt;&amp;gt; pool;

    auto dir(RefPtr&amp;lt;Node&amp;gt; x) -&amp;gt; int {
        assert(x &amp;amp;&amp;amp; x-&amp;gt;fat);
        return x == x-&amp;gt;fat-&amp;gt;son[1];
    }
    void pushUp(RefPtr&amp;lt;Node&amp;gt; x) {
        x-&amp;gt;sz = x-&amp;gt;cnt;
        if (x-&amp;gt;son[0]) x-&amp;gt;sz += x-&amp;gt;son[0]-&amp;gt;sz;
        if (x-&amp;gt;son[1]) x-&amp;gt;sz += x-&amp;gt;son[1]-&amp;gt;sz;
    }
    auto newNode(int v, RefPtr&amp;lt;Node&amp;gt; fat = nullptr) -&amp;gt; RefPtr&amp;lt;Node&amp;gt; {
        pool.push_back(std::make_unique&amp;lt;Node&amp;gt;());
        auto p = pool.back().get();
        p-&amp;gt;fat = fat, p-&amp;gt;val = v, p-&amp;gt;cnt = p-&amp;gt;sz = 1;
        return p;
    }

    void rotate(RefPtr&amp;lt;Node&amp;gt; x) {
        auto y = x-&amp;gt;fat;
        auto z = y-&amp;gt;fat;
        int r = dir(x);
        int d = z ? dir(y) : 0;
        y-&amp;gt;son[r] = x-&amp;gt;son[!r];
        if (x-&amp;gt;son[!r]) x-&amp;gt;son[!r]-&amp;gt;fat = y;
        x-&amp;gt;son[!r] = y;
        if (z) z-&amp;gt;son[d] = x;
        else rt = x;
        y-&amp;gt;fat = x;
        x-&amp;gt;fat = z;
        pushUp(y);
        pushUp(x);
    }

    void splay(RefPtr&amp;lt;Node&amp;gt; x) {
        if (!x) return ;
        while (x-&amp;gt;fat) {
            auto y = x-&amp;gt;fat;
            auto z = y-&amp;gt;fat;
            if (z) {
                if (dir(x) == dir(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        rt = x;
    }

    void find(int v) {
        auto x = rt;
        auto y = (RefPtr&amp;lt;Node&amp;gt;)nullptr;
        for (; x &amp;amp;&amp;amp; x-&amp;gt;val != v; y = x, x = x-&amp;gt;son[v &amp;gt; x-&amp;gt;val]);
        splay(x ? x : y);
    }

    void loc(int k) {
        auto x = rt;
        while (x) {
            int ls = x-&amp;gt;son[0] ? x-&amp;gt;son[0]-&amp;gt;sz : 0;
            if (k &amp;lt;= ls) {
                x = x-&amp;gt;son[0];
            } else if (k &amp;lt;= ls + x-&amp;gt;cnt) {
                break;
            } else {
                k -= ls + x-&amp;gt;cnt;
                x = x-&amp;gt;son[1];
            }
        }
        if (x) splay(x);
    }

    auto merge(RefPtr&amp;lt;Node&amp;gt; x, RefPtr&amp;lt;Node&amp;gt; y) -&amp;gt; RefPtr&amp;lt;Node&amp;gt; {
        if (!x || !y) {
            if (x) return x;
            return y;
        }
        rt = y;
        loc(1);
        rt-&amp;gt;son[0] = x;
        x-&amp;gt;fat = rt;
        pushUp(rt);
        return rt;
    }

public:
    RefPtr&amp;lt;Node&amp;gt; rt = nullptr;

    Splay() = default;
    ~Splay() {
        clear();
    }
    void clear() {
        rt = nullptr;
        std::vector&amp;lt;OwnPtr&amp;lt;Node&amp;gt;&amp;gt;().swap(pool);
    }

    void insert(int v) {
        auto x = rt, y = static_cast&amp;lt;RefPtr&amp;lt;Node&amp;gt;&amp;gt;(nullptr);
        for (; x &amp;amp;&amp;amp; x-&amp;gt;val != v; y = x, x = x-&amp;gt;son[v &amp;gt; x-&amp;gt;val]);
        if (x) x-&amp;gt;cnt++, x-&amp;gt;sz++;
        else {
            x = newNode(v, y);
            if (y) y-&amp;gt;son[v &amp;gt; y-&amp;gt;val] = x;
            else rt = x;
        }
        splay(x);
    }

    bool remove(int v) {
        find(v);
        if (!rt || rt-&amp;gt;val != v) return false;
        rt-&amp;gt;cnt--, rt-&amp;gt;sz--;
        if (!rt-&amp;gt;cnt) {
            auto x = rt-&amp;gt;son[0];
            auto y = rt-&amp;gt;son[1];
            if (x) x-&amp;gt;fat = nullptr;
            if (y) y-&amp;gt;fat = nullptr;
            rt = merge(x, y);
        }
        return true;
    }

    int find_rank(int v) {
        find(v);
        if (!rt) return 1;
        return (rt-&amp;gt;son[0] ? rt-&amp;gt;son[0]-&amp;gt;sz : 0) + (rt-&amp;gt;val &amp;lt; v ? rt-&amp;gt;cnt : 0) + 1;
    }

    int find_kth(int k) {
        if (!rt || k &amp;lt;= 0 || k &amp;gt; rt-&amp;gt;sz) return -1;
        loc(k);
        return rt-&amp;gt;val;
    }

    int find_prev(int v) {
        find(v);
        if (!rt) return -1;
        if (rt-&amp;gt;val &amp;lt; v) return rt-&amp;gt;val;
        auto x = rt-&amp;gt;son[0];
        if (!x) return -1;
        for (; x-&amp;gt;son[1]; x = x-&amp;gt;son[1]);
        splay(x);
        return rt-&amp;gt;val;
    }

    int find_next(int v) {
        find(v);
        if (!rt) return -1;
        if (rt-&amp;gt;val &amp;gt; v) return rt-&amp;gt;val;
        auto x = rt-&amp;gt;son[1];
        if (!x) return -1;
        for (; x-&amp;gt;son[0]; x = x-&amp;gt;son[0]);
        splay(x);
        return rt-&amp;gt;val;
    }
};

auto main() -&amp;gt; int {
    int T;
    std::cin &amp;gt;&amp;gt; T;
    Splay splay;
    while (T--) {
        int op, x;
        std::cin &amp;gt;&amp;gt; op &amp;gt;&amp;gt; x;
        switch (op) {
            case 1:
                splay.insert(x);
                break;
            case 2:
                splay.remove(x);
                break;
            case 3:
                std::cout &amp;lt;&amp;lt; splay.find_rank(x) &amp;lt;&amp;lt; &apos;\n&apos;;
                break;
            case 4:
                std::cout &amp;lt;&amp;lt; splay.find_kth(x) &amp;lt;&amp;lt; &apos;\n&apos;;
                break;
            case 5:
                std::cout &amp;lt;&amp;lt; splay.find_prev(x) &amp;lt;&amp;lt; &apos;\n&apos;;
                break;
            case 6:
                std::cout &amp;lt;&amp;lt; splay.find_next(x) &amp;lt;&amp;lt; &apos;\n&apos;;
                break;
        }
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Introduction of Splay</title><link>https://blog.517group.cn/posts/202605011204/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202605011204/</guid><description>Rotate last accessed node to root to maintain amortized efficiency.</description><pubDate>Fri, 08 May 2026 19:21:23 GMT</pubDate><content:encoded>&lt;h1&gt;What is Splay Tree?&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://old.517group.cn/posts/34701/&quot;&gt;Another blog about Balance tree&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The most common two balance trees is Splay and Treap. I already have introduced treap before so this article let&apos;s learn Splay.&lt;/p&gt;
&lt;p&gt;Unlike Treap, the basic operation of Splay is rotate. By rotating, we can let the node we want move to root. The core idea of Splay is using Splay function to let the node we recently visited depth get shallower.&lt;/p&gt;
&lt;h1&gt;What is its function?&lt;/h1&gt;
&lt;p&gt;It&apos;s a Balanced BST (Binary Search Tree), which can search, insert, delete a node in $O(\log n)$. And it can promise the tree balance.&lt;/p&gt;
&lt;p&gt;Before explain the core operation &lt;em&gt;Splay&lt;/em&gt;, You need know some basic sturcture and checking function.&lt;/p&gt;
&lt;h2&gt;Basic Structure&lt;/h2&gt;
&lt;p&gt;The example code in this article will using array to simulate pointer, the version more save will publish later.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable Name&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Id of root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the quantity of used node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fa[i]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;father of node $i$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ch[i][0/1]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left/right children of node $i$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;val[i]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value of node $i$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cnt[v]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The times value $v$ appear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sz[i]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The size of sub tree which root is $i$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Set all value to $0$ when initial.&lt;/p&gt;
&lt;h2&gt;Checking Function&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;bool dir(int x) {
    return x == ch[fa[x]][1]; // if x is left son return 0, or return 1.
}
void pushUp(int x) {
    sz[x] = cnt[x] + sz[ch[x][0]] + sz[ch[x][1]]; 
    // Update all information after changing position of node.
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;How to Rotate?&lt;/h1&gt;
&lt;p&gt;First we should know how the basic operation, rotate work.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/article_needed/202605011204/RotateSample.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This image told us how to rotate node $x$ in a tree. It&apos;s not difficlut to find that the inorder traversal of this tree have no changes. So rotate can not change the BST property of Splay tree. And it also a way to maintain the time complexity (or length) of Splay tree.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void rotate(int x) {
    int y = fa[x], z = fa[y];
    bool r = dir(x);
    ch[y][r] = ch[x][!r];
    ch[x][!r] = y;
    if (z) ch[z][dir(y)] = x;
    if (ch[y][r]) fa[ch[y][r]] = y;
    fa[y] = x;
    fa[x] = z;
    push_up(y);
    push_up(x);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Observe the picture I drawed can understand this function.&lt;/p&gt;
&lt;h1&gt;How to Splay?&lt;/h1&gt;
&lt;p&gt;Then, many rotate operation become a splay operation. There are some basic model when doing splay : Zig, Zig-Zig, Zig-Zag.&lt;/p&gt;
&lt;p&gt;We define function &lt;code&gt;Splay(x, k)&lt;/code&gt; denotes let node $x$ move up until be son of node $k$.&lt;/p&gt;
&lt;p&gt;Without thinking, do nothing if $x = k$.&lt;/p&gt;
&lt;h2&gt;Zig&lt;/h2&gt;
&lt;p&gt;This situation when $x$ is son of $k$. Just rotate $x$ is okay.&lt;/p&gt;
&lt;p&gt;In splay tree, we only do left rotate operation when $x$ is right son, and only do right rotate operation when $x$ is left son. That can lead us using one function to realize &lt;code&gt;Splay(x,k)&lt;/code&gt; in code.&lt;/p&gt;
&lt;h2&gt;Zig-Zig&lt;/h2&gt;
&lt;p&gt;This situation when $x$ and $fa_x$ is son in same side of their father. We should first rotate $fa_x$ then rotate $x$, we can do a little contrast.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/article_needed/202605011204/ContrastSample.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Why not rotate $x$ twice? That because it will change the relative position of $fa_x$ and $x$. Using correct Zig-Zig plan can let the final struction also Zig-Zig, and this plan also get best time complexity when potential method&lt;/p&gt;
&lt;h2&gt;Zig-Zag&lt;/h2&gt;
&lt;p&gt;This situation when $x$ and $fa_x$ is son in different side of their father. We can rotate $x$ twice in this situation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/article_needed/202605011204/ZigZagSample.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We can found the height of this tree decrease.&lt;/p&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;p&gt;Then we know all the operator in each situation we can write the code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void splay(int&amp;amp; z, int x) {
    int w = fa[z];
    for (int y; (y = fa[x]) != w; rotate(x)) { // whatever which situation, the last operation is rotate x
        if (fa[y] != w)  // if zigzig or zigzag
            rotate(dir(x) == dir(y) ? y : x); // check it&apos;s zigzig or zigzag
    }
    z = x;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;How Splay Tree Do Basic Operation of Balance Tree?&lt;/h1&gt;
&lt;p&gt;And below is how splay to realize some classic operation.&lt;/p&gt;
&lt;h2&gt;Find with Value&lt;/h2&gt;
&lt;p&gt;Because Splay Tree also a BST, so the process find a node with value is same as BST, a only difference is we need splay node we found to root.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// find value v; if not found, end at last accessed node
void find(int&amp;amp; z, int v) {
    int x = z, y = 0; // x: current node, y: last visited node
    for (; x &amp;amp;&amp;amp; val[x] != v; x = ch[y = x][v &amp;gt; val[x]]);
    // traverse BST; stop when found or reach null
    splay(z, x ? x : y);
    // splay the found node, or the last accessed node to root
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Find with Rank&lt;/h2&gt;
&lt;p&gt;Same as treap too.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void loc(int&amp;amp; z, int k) {
    int x = z;
    for (;;) {
        if (sz[ch[x][0]] &amp;gt;= k) {
            x = ch[x][0];
        } else if (sz[ch[x][0]] + cnt[x] &amp;gt;= k) {
            break; 
        } else {
            k -= sz[ch[x][0]] + cnt[x];
            x = ch[x][1];
        }
    }
    splay(z, x);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Insert a Value&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;void insert(int v) { // insert value v
    int x = rt, y = 0;
    for (; x &amp;amp;&amp;amp; val[x] != v; x = ch[y = x][v &amp;gt; val[x]]);
    // find a node with value 
    if (x) { // if already have this node (isn&apos;t empty node &amp;amp; value is wanted)
        ++cnt[x];
        ++sz[x];
    } else { // is a empty node
        x = ++id; // new node &amp;amp; initialize
        val[x] = v;
        cnt[x] = sz[x] = 1;
        fa[x] = y;
        if (y) ch[y][v &amp;gt; val[y]] = x; // insert it
    }
    splay(rt, x); // splay it to root
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Merge Two Slay Trees&lt;/h2&gt;
&lt;p&gt;This operation really like treap.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int merge(int x, int y) {
    if (!x || !y) return x | y;
    loc(y, 1); // find the minimum of tree y, it must have no left children
    ch[y][0] = x;
    fa[x] = y;
    push_up(y);
    return y; // return now root
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Remove a Value&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;bool remove(int v) {
    find(rt, v);
    if (!rt || val[rt] != v) return false;
    --cnt[rt];
    --sz[rt];
    if (!cnt[rt]) {
        int x = ch[rt][0];
        int y = ch[rt][1];
        fa[x] = fa[y] = 0;
        rt = merge(x, y);
    }
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Query Rank&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int find_rank(int v) {
    find(rt, v); // we cannot sure result find returned must greater or less than v when v is not exist
    return sz[ch[rt][0]] + (val[rt] &amp;lt; v ? cnt[rt] : 0) + 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Query Prevent and Next&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int find_prev(int v) {
    find(rt, v); // rt exist and val[rt] less than v
    if (rt &amp;amp;&amp;amp; val[rt] &amp;lt; v) return val[rt];
    int x = ch[rt][0]; // this situation when v exist or have a node which greater than v
    if (!x) return -1; // so check now node&apos;s left subtree
    for (; ch[x][1]; x = ch[x][1]); // the maximum of left subtree is rightest node
    splay(rt, x);
    return val[rt];
}

// same with find_prev
int find_next(int v) {
    find(rt, v);
    if (rt &amp;amp;&amp;amp; val[rt] &amp;gt; v) return val[rt];
    int x = ch[rt][1];
    if (!x) return -1;
    for (; ch[x][0]; x = ch[x][0]);
    splay(rt, x);
    return val[rt];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;All Code&lt;/h2&gt;
&lt;p&gt;From &lt;a href=&quot;https://oi-wiki.org/ds/splay/#%E5%8F%82%E8%80%83%E5%AE%9E%E7%8E%B0&quot;&gt;OI wiki&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

constexpr int N = 2e6;
int id, rt;
int fa[N], val[N], cnt[N], sz[N], ch[N][2];

bool dir(int x) { return x == ch[fa[x]][1]; }

void push_up(int x) { sz[x] = cnt[x] + sz[ch[x][0]] + sz[ch[x][1]]; }

void rotate(int x) {
  int y = fa[x], z = fa[y];
  bool r = dir(x);
  ch[y][r] = ch[x][!r];
  ch[x][!r] = y;
  if (z) ch[z][dir(y)] = x;
  if (ch[y][r]) fa[ch[y][r]] = y;
  fa[y] = x;
  fa[x] = z;
  push_up(y);
  push_up(x);
}

void splay(int&amp;amp; z, int x) {
  int w = fa[z];
  for (int y; (y = fa[x]) != w; rotate(x)) {
    if (fa[y] != w) rotate(dir(x) == dir(y) ? y : x);
  }
  z = x;
}

void find(int&amp;amp; z, int v) {
  int x = z, y = fa[x];
  for (; x &amp;amp;&amp;amp; val[x] != v; x = ch[y = x][v &amp;gt; val[x]]);
  splay(z, x ? x : y);
}

void loc(int&amp;amp; z, int k) {
  int x = z;
  for (;;) {
    if (sz[ch[x][0]] &amp;gt;= k) {
      x = ch[x][0];
    } else if (sz[ch[x][0]] + cnt[x] &amp;gt;= k) {
      break;
    } else {
      k -= sz[ch[x][0]] + cnt[x];
      x = ch[x][1];
    }
  }
  splay(z, x);
}

int merge(int x, int y) {
  if (!x || !y) return x | y;
  loc(y, 1);
  ch[y][0] = x;
  fa[x] = y;
  push_up(y);
  return y;
}

void insert(int v) {
  int x = rt, y = 0;
  for (; x &amp;amp;&amp;amp; val[x] != v; x = ch[y = x][v &amp;gt; val[x]]);
  if (x) {
    ++cnt[x];
    ++sz[x];
  } else {
    x = ++id;
    val[x] = v;
    cnt[x] = sz[x] = 1;
    fa[x] = y;
    if (y) ch[y][v &amp;gt; val[y]] = x;
  }
  splay(rt, x);
}

bool remove(int v) {
  find(rt, v);
  if (!rt || val[rt] != v) return false;
  --cnt[rt];
  --sz[rt];
  if (!cnt[rt]) {
    int x = ch[rt][0];
    int y = ch[rt][1];
    fa[x] = fa[y] = 0;
    rt = merge(x, y);
  }
  return true;
}

int find_rank(int v) {
  find(rt, v);
  return sz[ch[rt][0]] + (val[rt] &amp;lt; v ? cnt[rt] : 0) + 1;
}

int find_kth(int k) {
  if (k &amp;gt; sz[rt]) return -1;
  loc(rt, k);
  return val[rt];
}

int find_prev(int v) {
  find(rt, v);
  if (rt &amp;amp;&amp;amp; val[rt] &amp;lt; v) return val[rt];
  int x = ch[rt][0];
  if (!x) return -1;
  for (; ch[x][1]; x = ch[x][1]);
  splay(rt, x);
  return val[rt];
}

int find_next(int v) {
  find(rt, v);
  if (rt &amp;amp;&amp;amp; val[rt] &amp;gt; v) return val[rt];
  int x = ch[rt][1];
  if (!x) return -1;
  for (; ch[x][0]; x = ch[x][0]);
  splay(rt, x);
  return val[rt];
}

int main() {
  int n;
  std::cin &amp;gt;&amp;gt; n;
  for (; n; --n) {
    int op, x;
    std::cin &amp;gt;&amp;gt; op &amp;gt;&amp;gt; x;
    switch (op) {
      case 1:
        insert(x);
        break;
      case 2:
        remove(x);
        break;
      case 3:
        std::cout &amp;lt;&amp;lt; find_rank(x) &amp;lt;&amp;lt; &apos;\n&apos;;
        break;
      case 4:
        std::cout &amp;lt;&amp;lt; find_kth(x) &amp;lt;&amp;lt; &apos;\n&apos;;
        break;
      case 5:
        std::cout &amp;lt;&amp;lt; find_prev(x) &amp;lt;&amp;lt; &apos;\n&apos;;
        break;
      case 6:
        std::cout &amp;lt;&amp;lt; find_next(x) &amp;lt;&amp;lt; &apos;\n&apos;;
        break;
    }
  }
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Solution Report of String(Middle) Topic</title><link>https://blog.517group.cn/posts/202604042043/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202604042043/</guid><description>Hard.</description><pubDate>Wed, 29 Apr 2026 21:06:35 GMT</pubDate><content:encoded>&lt;h1&gt;P12923 [POI 2021/2022 R3] 模板 2 / Szablon 2&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P12923&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/vakgr3sm&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can found that if a chain of expanding just contribute length less than half, they all can be replaced by the last one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.luogu.com.cn/upload/image_hosting/0n83p1oj.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then we can using ExKMP to calculate the LCP of origin string and adverse string, knowing that which place should not place string.&lt;/p&gt;
&lt;p&gt;The last part we can greedy, the strategy is find the most back position which can compare a origin string or adverse string, it is easy. But there I know a new way to maintain this position which is using DSU instead SegTree.&lt;/p&gt;
&lt;p&gt;At first each node point to themselves. Then if note $x$ become invalid, let &lt;code&gt;fa[x] = find(x-1)&lt;/code&gt;. So the root of each DSU is the most back position which valid.&lt;/p&gt;
&lt;h1&gt;P11291 【MX-S6-T3】「KDOI-11」简单的字符串问题 2&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P11291&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/ftdd4xx2&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;P10716 【MX-X1-T4】「KDOI-05」简单的字符串问题&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P10716&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/yqrbdxme&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We can prove that a string &lt;code&gt;A&lt;/code&gt; is valid if and only if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;A&lt;/code&gt; is a border of &lt;code&gt;S[1..i]&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A&lt;/code&gt; appears at least &lt;code&gt;k&lt;/code&gt; times in &lt;code&gt;S[1..i]&lt;/code&gt; without overlap.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After building the failure tree, we can observe that all valid &lt;code&gt;A&lt;/code&gt; form a chain from some node &lt;code&gt;u&lt;/code&gt; to the root, and &lt;code&gt;u&lt;/code&gt; is an ancestor of &lt;code&gt;i&lt;/code&gt;. Therefore, once we know &lt;code&gt;u&lt;/code&gt;, the answer is simply &lt;code&gt;dep[u]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can find &lt;code&gt;u&lt;/code&gt; using binary lifting.&lt;br /&gt;
This is equivalent to checking the following condition:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Whether the position of the &lt;code&gt;k&lt;/code&gt;-th non-overlapping occurrence of &lt;code&gt;S[1..u]&lt;/code&gt; is ≤ &lt;code&gt;i&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So we preprocess, for every prefix &lt;code&gt;S[1..u]&lt;/code&gt;, the positions where this string appears in the whole string without overlap. This is equivalent to repeatedly finding the first suffix after the current position whose LCP with &lt;code&gt;S&lt;/code&gt; is at least &lt;code&gt;u&lt;/code&gt;, and then jumping to that suffix.&lt;/p&gt;
&lt;p&gt;The total number of jumps is at most:&lt;/p&gt;
&lt;p&gt;$$
\sum_{i=1}^{n} \frac{n}{i} = O(n \log n)
$$&lt;/p&gt;
&lt;p&gt;so we can just simulate the jumps directly.&lt;/p&gt;
&lt;p&gt;To compute the LCP between a suffix and &lt;code&gt;S&lt;/code&gt;, we can use the Z-function.&lt;br /&gt;
Then we apply the DSU trick to maintain a linked list: enumerate &lt;code&gt;u&lt;/code&gt; from small to large, and after finishing &lt;code&gt;u&lt;/code&gt;, delete all positions &lt;code&gt;p&lt;/code&gt; with &lt;code&gt;z[p] = u&lt;/code&gt;. This guarantees that when processing &lt;code&gt;u&lt;/code&gt;, the DSU representative of a position is exactly the next position with &lt;code&gt;z[p] \ge u&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this way, we can find in &lt;code&gt;O(\alpha(n))&lt;/code&gt; the first position &lt;code&gt;p&lt;/code&gt; after a given index such that &lt;code&gt;z[p] ≥ u&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note that the case &lt;code&gt;k = 1&lt;/code&gt; needs to be handled separately.&lt;/p&gt;
&lt;p&gt;Thus the total time complexity is:&lt;/p&gt;
&lt;p&gt;$$
O(q \log n + n \alpha(n) \log n)
$$&lt;/p&gt;
&lt;h1&gt;P9482 [NOI2023] 字符串&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P9482&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/67y2662p&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;P6125 [JSOI2009] 有趣的游戏&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P6125&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/b9o7hxjc&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&apos;s not hard to think of using ACAM to solve this problem. But the difficlut point is if the state denote probability when through a node on ACAM, but ACAM have cycle so we cannot use it.&lt;/p&gt;
&lt;p&gt;Consider below things: We stop when across a &lt;em&gt;mark point&lt;/em&gt; on ACAM, so we can let state to calculate expectation. Because we stop when meet first mark point, so we can promise the sum of expectation of each node equal to $1$. Expectation now is equal to probability.&lt;/p&gt;
&lt;p&gt;Left part is easy, just use gaussian elimination solve it.&lt;/p&gt;
&lt;h1&gt;P5466 [PKUSC2018] 神仙的游戏&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P5466&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/i80intxg&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We found that if a string $s$ have a border length $len$, it will have a cyclic section with length $n-len$. So wildcard become meanless because we can calculate them by cyclic section.&lt;/p&gt;
&lt;p&gt;To be more, if $i$ and $j$ are equal modulo $n-len$, $s_i$ and $s_j$ are must equal, or $len$ is impossible.&lt;/p&gt;
&lt;p&gt;So we can define two array:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;$a[i]$, it will equal $1$ only when $s[i]$ is equal $1$.&lt;/li&gt;
&lt;li&gt;$b[i]$, it will equal $1$ only when $s[i]$ is equal $0$.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then we can use&lt;/p&gt;
&lt;p&gt;$$
C[d] = \sum_{i=1}^n A[i]\times B[i+d]
$$&lt;/p&gt;
&lt;p&gt;to count there are different number pair in length $d$.&lt;/p&gt;
&lt;p&gt;left part is easy, just check a length $len$ is valid or not then add it contribution to answer.&lt;/p&gt;
&lt;h1&gt;P4569 [BJWC2011] 禁忌&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P4569&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/ud0jpeti&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The optimal strategy is greedy.&lt;/p&gt;
&lt;p&gt;Suppose we have already matched a taboo string ending at position &lt;code&gt;i&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If we cut immediately, we gain &lt;code&gt;+1&lt;/code&gt;, and restart.&lt;/li&gt;
&lt;li&gt;If we delay, this segment may overlap with future matches, possibly reducing total count.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thus: Cut immediately whenever a taboo string appears is optimal.&lt;/p&gt;
&lt;p&gt;We build an &lt;strong&gt;AC Automaton&lt;/strong&gt; over all taboo strings.&lt;/p&gt;
&lt;p&gt;Each state represents:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;current suffix match&lt;/li&gt;
&lt;li&gt;whether we have matched a taboo string (&lt;code&gt;is_taboo&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And do &lt;code&gt;is_taboo[u] |= is_taboo[fail[u]]&lt;/code&gt;. So any suffix match is correctly detected.&lt;/p&gt;
&lt;p&gt;Let:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;state&lt;/code&gt; denotes current node in AC automaton&lt;/li&gt;
&lt;li&gt;plus one extra dimension: &lt;strong&gt;accumulated expectation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So total dimension is &lt;code&gt;states + 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Since the string is random, each character is chosen with probability &lt;code&gt;1 / alphabet&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Enumerate &lt;code&gt;(u, c)&lt;/code&gt;, let &lt;code&gt;v = next(u, c)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;v&lt;/code&gt; is not taboo, just transfer &lt;code&gt;u → v&lt;/code&gt;.&lt;br /&gt;
If &lt;code&gt;v&lt;/code&gt; is taboo, we greedily cut here, so &lt;code&gt;u → root&lt;/code&gt; and contribute &lt;code&gt;+1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To handle expectation, add one extra dimension &lt;code&gt;ans_col&lt;/code&gt;.&lt;br /&gt;
When hitting a taboo, do &lt;code&gt;trans[u][ans_col] += 1 / alphabet&lt;/code&gt; and let &lt;code&gt;trans[ans_col][ans_col] = 1&lt;/code&gt; so the answer accumulates.&lt;/p&gt;
&lt;p&gt;Now the whole process becomes a linear transfer:&lt;/p&gt;
&lt;p&gt;$$
f_{t+1} = f_t × trans
$$&lt;/p&gt;
&lt;p&gt;We need exactly length &lt;code&gt;len&lt;/code&gt;, so compute &lt;code&gt;trans^len&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Starting from root, the answer is &lt;code&gt;(trans^len)[root][ans_col]&lt;/code&gt;.&lt;br /&gt;
Time complexity is $O(S^3 log len)$ where $S$ is number of AC states.&lt;/p&gt;
&lt;h1&gt;P4081 [USACO17DEC] Standing Out from the Herd P&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P4081&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/i7em3ard&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is common trick when SA is solving a problem with lot of string. Let these string end to end, and using a character which not in character set to connect them.&lt;/p&gt;
&lt;p&gt;Let the initial answer equal to $\frac{len\times(len+1)}{2}$.&lt;/p&gt;
&lt;p&gt;For each suffix $x$, the excluded answer is $\max(\operatorname{lcp}(x,i),\operatorname{lcp}(x,j),\operatorname{lcp}(x,k))$, where $i$ is the suffix with the largest rank satisfying $rk[i]&amp;lt;rk[x], id[i]\neq id[x]$, $j$ is the suffix with the smallest rank satisfying $rk[j]&amp;gt;rk[x], id[j]\neq id[x]$, and $k$ is the suffix with the largest rank satisfying $id[k]=id[x]$.&lt;/p&gt;
&lt;p&gt;Then we can find &lt;code&gt;hight&lt;/code&gt; array in SA is equal to excluded part. So just minus them.&lt;/p&gt;
&lt;h1&gt;P4465 [国家集训队] JZPSTR&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P4465&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/ew1fgtds&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It said that std of this problem is Unrolled Linked List + SAM, but most solution in Luogu using bitset to solve it and get a great score.&lt;/p&gt;
&lt;p&gt;Because the character set is little just 10, we let &lt;code&gt;std::bitset&amp;lt;N&amp;gt; b[x]&lt;/code&gt; denote character $x$ is at $pos$ if &lt;code&gt;b[x][pos] = 1&lt;/code&gt;. Then we can maintain each oeprate easily.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;right move origin string length of $y_i$ from $x_i$, then put $y_i$ in the blank.&lt;/li&gt;
&lt;li&gt;same with above, left move origin string length of $y_i - x_i + 1$.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then how to match a string become the main problem. Repeat &lt;code&gt;ans = (ans &amp;lt;&amp;lt; 1) &amp;amp; b[z[j]-&apos;0&apos;]&lt;/code&gt; then the number of $1$ is answer, draw the process then you can understand, this algorithm called Shift-and.&lt;/p&gt;
&lt;h1&gt;P4045 [JSOI2009] 密码&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P4045&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;s&gt;Reference Blog&lt;/s&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Classic problem, do compression DP on ACAM.&lt;/p&gt;
&lt;p&gt;Let &lt;code&gt;dp[i][j][msk]&lt;/code&gt; denotes when we contract to place $i$ and we are on the node $j$ ACAM and the usage state of preparing string is $msk$. Then transition is easy.&lt;/p&gt;
&lt;p&gt;$$
dp[i][j][msk] \rightarrow dp[i+1][tr_{j,l}][msk|state_{tr_{j,l}}]
$$&lt;/p&gt;
&lt;p&gt;$tr_{j,l}$ denotes the son of node $j$ which is meaning letter $l$.&lt;/p&gt;
&lt;p&gt;The answer is sum of $dp[n][j][fullmask]$.&lt;/p&gt;
&lt;h1&gt;P3823 [NOI2017] 蚯蚓排队&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3823&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/i57xokd8&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Becasue $k$ is very little, we can maintain answer of each $k$ when hashtable is merging and splitting.&lt;/p&gt;
&lt;p&gt;We can maintain this queue by list, and answer only related to $k$. For example, if we are merging two queue, just enumerate the length $l$ and enumerate start point $j$ then count all string that two endpoints are on both sides of the splice.&lt;/p&gt;
&lt;p&gt;The time complexity is $O(nk + ck^2 + \sum |s|)$, $c$ is the number of second operation. Because each operate will just produce $k^2$ hash value at most.&lt;/p&gt;
&lt;h1&gt;P1117 [NOI2016] 优秀的拆分&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P1117&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/cojjoptx&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;P3234 [HNOI2014] 抄卡组&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3234&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/pmisez51&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Analyze in 3 cases.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All string have no &lt;code&gt;*&lt;/code&gt;, compare them with hash.&lt;/li&gt;
&lt;li&gt;All string have &lt;code&gt;*&lt;/code&gt;, check substring from 0 to first &lt;code&gt;*&lt;/code&gt; is prefix of next string, and the substring from last &lt;code&gt;*&lt;/code&gt; to end is suffix of next string. Remember sort with length.&lt;/li&gt;
&lt;li&gt;Both two case. First, check same as case 1. Then we can use hash check beginning, middle, ending part of this string.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;P3715 [BJOI2017] 魔法咒语&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3715&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/7n1nyh5r&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Also a classic problem, similar as P4045 but this problem should split into two part to solve it.&lt;/p&gt;
&lt;p&gt;First for small $L$, do compress DP on ACAM, the state $dp[len][j]$ denotes we already get new magic which length is $len$ and we are at node $j$ on ACAM. Transition is easy. Enumerate all string if there are no bad word, we can transition.&lt;/p&gt;
&lt;p&gt;Then try think how to solve this problem when $L &amp;lt;= 10^8$, first we can know that we must use matrix multiple to solve it when $L$ become such so big. So we have below idea:&lt;/p&gt;
&lt;h1&gt;$$
\left[\begin{matrix}
F_t \ F_{t+1}
\end{matrix}\right]&lt;/h1&gt;
&lt;p&gt;\operatorname{Matrix}
\times
\left[\begin{matrix}
F_{t-1} \ F_{t}
\end{matrix}\right]
$$&lt;/p&gt;
&lt;p&gt;Then their is a common trick when construct matrix: if $i$ can transition to $j$, &lt;code&gt;mat[i][j]++&lt;/code&gt;.&lt;br /&gt;
Same as first case, transition is easy, then we can get transition matrix.&lt;/p&gt;
&lt;h1&gt;P3245 [HNOI2016] 大数&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3245&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/8rzcesm7&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We using $sum[i]$ denotes the value of $S[i\dots n]$.&lt;/p&gt;
&lt;p&gt;Also we can analyze into $2$ cases: $P$ equal or not equal to $2$ and $5$.&lt;/p&gt;
&lt;p&gt;If is not:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;amp;\ \frac{sum[L] - sum[R+1]}{10^{n-R}} \bmod P = 0\
\Rightarrow &amp;amp;\  (sum[L] - sum[R+1]) \bmod P = 0 \
\Rightarrow &amp;amp;\  sum[L] \equiv sum[R+1] \pmod{P} \
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;Then problem turn to count how many same number in $sum[L\dots R+1]$, it a classic problem of Mo&apos;s algorithm.&lt;/p&gt;
&lt;p&gt;If is:&lt;/p&gt;
&lt;p&gt;It become easier: if the end of a number can be divided by $2$ or $5$ then whole number can be divided by it, so answer increase $i$ if $a[i] \bmod P = 0$.&lt;/p&gt;
&lt;h1&gt;P3546 [POI 2012] PRE-Prefixuffix&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3546&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/bghgaezw&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because of the length-$L$ prefix and suffix can be represented as &lt;code&gt;XY&lt;/code&gt; and &lt;code&gt;YX&lt;/code&gt;, if we using $k$ denote the cut position between $X$ and $Y$. Then we have : $s[1\cdots k]=s[n-k+1\cdots n],s[k+1\cdots L]=s[n-L+1\cdots n-k]$. This means that problem wants to us calculate two borders.&lt;/p&gt;
&lt;p&gt;then is a interesting transition: turn origin string into form $s_1,s_n,s_2,s_{n-1},\cdots$, two borders problem let we calculate become two palindromes on top. So using manacher can solve this problem easily.&lt;/p&gt;
&lt;h1&gt;P3454 [POI 2007] OSI-Axes of Symmetry&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3454&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/bghgaezw&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If a shape is symmetrical, we can get a palindrome if cut from a endpoint of axis of symmetry.&lt;/p&gt;
&lt;p&gt;So use cross product instead of angle, the square of an edge&apos;s length instead of length of edge. Then manacher to count how many palindrome can cover a length $n$ substring in the double-string. double-string means we paste a copy of origin string at the end of origin string.&lt;/p&gt;
</content:encoded></item><item><title>Template of Aho-Corasick AutoMaton</title><link>https://blog.517group.cn/posts/202604062214/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202604062214/</guid><description>meaningful</description><pubDate>Mon, 06 Apr 2026 22:14:49 GMT</pubDate><content:encoded>&lt;p&gt;I learned &lt;em&gt;Ownner Pointer&lt;/em&gt; and &lt;em&gt;Reference Pointer&lt;/em&gt; today, and use them to write a safe template of ACAM.&lt;/p&gt;
&lt;p&gt;The process of algorithm is needless to say.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename Tp&amp;gt;
using OwnPointer = std::unique_ptr&amp;lt;Tp&amp;gt;;
template &amp;lt;typename Tp&amp;gt;
using RefPointer = Tp*;

constexpr int MAXN = 6;
constexpr int ALPHABET = 26;

class ACAM {
public:
    explicit ACAM(const std::vector&amp;lt;std::string&amp;gt;&amp;amp; set) {
        Build(set);
    }
private:
    struct TrieNode {
        std::array&amp;lt;RefPointer&amp;lt;TrieNode&amp;gt;, ALPHABET&amp;gt; ch;
        RefPointer&amp;lt;TrieNode&amp;gt; fail;
        bool is_taboo;

        TrieNode() {
            ch.fill(nullptr);
            fail = nullptr;
            is_taboo = false;
        }
    };
    std::vector&amp;lt;OwnPointer&amp;lt;TrieNode&amp;gt;&amp;gt; node_pool;
    RefPointer&amp;lt;TrieNode&amp;gt; root;

    RefPointer&amp;lt;TrieNode&amp;gt; NewNode() {
        node_pool.emplace_back(std::make_unique&amp;lt;TrieNode&amp;gt;());
        return node_pool.back().get();
    }

    void Insert(const std::string&amp;amp; s) {
        RefPointer&amp;lt;TrieNode&amp;gt; now = root;
        for(const char&amp;amp; c : s) {
            int id = c-&apos;a&apos;;
            if(!now-&amp;gt;ch[id]) {
                now-&amp;gt;ch[id] = NewNode();
            }
            now=now-&amp;gt;ch[id];
        }
        now-&amp;gt;is_taboo = true;
    }

    void Build(const std::vector&amp;lt;std::string&amp;gt;&amp;amp; set) {
        root = NewNode();
        for(const std::string &amp;amp;s : set) Insert(s);
        std::queue&amp;lt;RefPointer&amp;lt;TrieNode&amp;gt;&amp;gt; q;
        root-&amp;gt;fail = root;
        for(int i=0;i&amp;lt;ALPHABET;i++) {
            if(root-&amp;gt;ch[i]) {
                root-&amp;gt;ch[i]-&amp;gt;fail = root;
                q.push(root-&amp;gt;ch[i]);
            } else root-&amp;gt;ch[i] = root;
        }
        while(!q.empty()) {
            auto u = q.front(); q.pop();
            u-&amp;gt;is_taboo |= u-&amp;gt;fail-&amp;gt;is_taboo;
            for(int i{0}; i &amp;lt; ALPHABET; i++) {
                if(u-&amp;gt;ch[i]) {
                    u-&amp;gt;ch[i]-&amp;gt;fail = u-&amp;gt;fail-&amp;gt;ch[i];
                    q.push(u-&amp;gt;ch[i]);
                } else u-&amp;gt;ch[i] = u-&amp;gt;fail-&amp;gt;ch[i];
            }
        }
    }
public:
    auto Root() const -&amp;gt; RefPointer&amp;lt;TrieNode&amp;gt; {
        return root;
    }
    auto Next(RefPointer&amp;lt;TrieNode&amp;gt; u, char c) const -&amp;gt; RefPointer&amp;lt;TrieNode&amp;gt; {
        return u-&amp;gt;ch[c-&apos;a&apos;];
    }
    auto IsTaboo(RefPointer&amp;lt;TrieNode&amp;gt; u) const -&amp;gt; bool {
        return u-&amp;gt;is_taboo;
    }
    auto Size() const -&amp;gt; int {
        return node_pool.size();
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;s&gt;Remember that u are solving LuoguP4569 that time, &lt;code&gt;Taboo&lt;/code&gt; means unallowed string.&lt;/s&gt;&lt;/p&gt;
</content:encoded></item><item><title>Solution Report of String(Easy) Topic</title><link>https://blog.517group.cn/posts/202603121938/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202603121938/</guid><description>Easy</description><pubDate>Thu, 26 Mar 2026 19:38:13 GMT</pubDate><content:encoded>&lt;h1&gt;LuoguP13270 最小表示法&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P13270&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/sl2n7n1z&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Hash Tech: How to compare string with dictionary order&lt;/h2&gt;
&lt;p&gt;Hash &amp;amp; Binary Lifting find the LCP of two string and then compare the next character.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::string s, t;
int len{0};
for (int k{19}; k &amp;gt;= 0; k--) {
    if (getHash(s, 0, len+(1&amp;lt;&amp;lt;k)-1) == getHash(t, 0, len+(1&amp;lt;&amp;lt;k)-1)) {
        len += (1 &amp;lt;&amp;lt; k);
    }
}
if (len == std::min(s.size(), t.size())) ; // s == t
else ; // s[len] &amp;lt; t[len]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;*&lt;em&gt;above code could be wrong&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;It&apos;s difficult to solve a problem on circle so copy string behind it.&lt;/p&gt;
&lt;p&gt;Now we should solve below problem:&lt;br /&gt;
Find $i$ hold $\min s[i\dots i+n-1]$&lt;/p&gt;
&lt;p&gt;Then we consider how to check it quickly. When we are checking the cyclic isomorphism start with $st$ and $i$.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First we find the maximum $j$ hold $s[st\dots st+j-1] = s[i\dots i+j-1]$.&lt;/li&gt;
&lt;li&gt;Then compare the character $s[st+j]$ and $s[i+j]$.
&lt;ul&gt;
&lt;li&gt;if $s[st+j]$ is better than $s[i+j]$, all string start with $i \le k \le i+j-1$ cannot be answer.&lt;/li&gt;
&lt;li&gt;otherwise, move $i$ to $\max(st+j+1, i+1)$.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
const int maxn=2e7;
int n;
string s;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin&amp;gt;&amp;gt;n&amp;gt;&amp;gt;s,s=&quot; &quot;+s+s;
	int st=1;
	for(int i=2;i&amp;lt;=n;){
		int j=0;
		for(j=0;j&amp;lt;n &amp;amp;&amp;amp; s[st+j]==s[i+j];j++);
		if(j==n) break;
		if(s[st+j]&amp;gt;s[i+j]){
            int m=st;
            st=i,i=max(i+1,m+j+1);
        }
		else i+=j+1;
	}
	for(int i=st;i&amp;lt;=st+n-1;i++) cout&amp;lt;&amp;lt;s[i];
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;LuoguP9873 [EC Final 2021] Beautiful String&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P9873&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/u7fvh865&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We found that beautiful string is of the form &lt;code&gt;AABCAB&lt;/code&gt;, so we can calculate answer by count &lt;code&gt;AB&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then we can define below two array:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Let $f[i][j]$ denote, when &lt;code&gt;AB&lt;/code&gt; $= S[i\dots i + j − 1]$, the number of substrings equal to &lt;code&gt;AB&lt;/code&gt; that start after position $i + j$.&lt;/li&gt;
&lt;li&gt;Let $g[i][j]$ denote, when &lt;code&gt;AB&lt;/code&gt; $= S[i\dots i + j − 1]$, the number of substrings before &lt;code&gt;AB&lt;/code&gt; that are prefixes of &lt;code&gt;AB&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then the answer is $\sum f[i][j]\times g[i][j]$.&lt;/p&gt;
&lt;p&gt;Now consider how to calculate these array.&lt;/p&gt;
&lt;p&gt;For $f$, calculate the LCP length of all pair of $s[i\dots n]$ and $s[j\dots n]$ then save them in $f[i][LCP]$, suffixing f.&lt;br /&gt;
For $g$, count the number of LCP of $s[i\dots n]$ and $s[i-j\dots n]$ which length is greater than $j$.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cin &amp;gt;&amp;gt; S;
n = S.length(); S = &apos; &apos; + S;
for(int i = n; i; i--)
    for(int j = i; j &amp;lt;= n; j++)
        if(S[i] == S[j])
            lcp[i][j] = lcp[i + 1][j + 1] + 1;
for(int i = 2; i &amp;lt;= n; i++)
    for(int j = i + 3; j &amp;lt; n; j++) {
        int k = min(j - i - 1, lcp[i][j]);
        if(k &amp;gt;= 2) f[i][k]++;
    }
for(int i = 2; i &amp;lt;= n; i++)
    for(int j = n - 1; j &amp;gt; 1; j--) f[i][j] += f[i][j + 1];
for(int i = 2; i &amp;lt;= n; i++)
    for(int j = 1; j &amp;lt;= min(i - 1, n - i + 1); j++) 
        if(lcp[i - j][i] &amp;gt;= j) g[i][j]++;
for(int i = 1; i &amp;lt;= n; i++)
    for(int j = 1; j &amp;lt;= n; j++) g[i][j] += g[i][j - 1];
long long ans = 0;
for(int i = 2; i &amp;lt;= n; i++)
    for(int j = 2; j &amp;lt;= n; j++) ans += 1ll * f[i][j] * g[i][j - 1];
cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;LuoguP9016 [USACO23JAN] Find and Replace G&lt;/h1&gt;
&lt;p&gt;These kind of question which after operation can cover early operation we can all consider it from back to front.&lt;/p&gt;
&lt;p&gt;We can build a DAG to solve this question. Use examples as demonstrations.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a ab
a bc
c de
b bbb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build 5 nodes for letter &lt;code&gt;a&lt;/code&gt; to &lt;code&gt;e&lt;/code&gt;, then build graph from last operation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      (7,#,sz=3)
       /      \
 (6,#,2)      (2,&apos;b&apos;)
   /    \
(2,&apos;b&apos;) (2,&apos;b&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keep the structure of 2-branch tree, but reuse same node.&lt;/p&gt;
&lt;p&gt;Below is operator &lt;code&gt;c de&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   (8,#,2)
   /    \
(4,&apos;d&apos;)(5,&apos;e&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can see how to combine two subtree (operator &lt;code&gt;a bc&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;           (9,#,5)
          /       \
     (7,#,3)    (8,#,2)
      /   \      /    \
  (6,#)    b    d      e
   / \
  b   b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Final version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                    (10,#,8)
                   /        \
             (9,#,5)      (7,#,3)
            /      \      /      \
       (7,#,3)  (8,#,2) (6,#)    b
        /   \    /   \   /  \
    (6,#)   b   d     e b    b
     / \
    b   b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can get answer from the tree size.&lt;/p&gt;
&lt;h1&gt;LuoguP7114 [NOIP2020] 字符串匹配&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P7114&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/bti82yh8&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;It&apos;s a really perfect tutorial. I don&apos;t even know what else to supplement.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We define the $i$-th element of Z-array denote the length of LCP of $s[0\dots n-1]$ and $s[i\dots n-1]$.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.luogu.com.cn/upload/image_hosting/16k5feoc.png&quot; alt=&quot;Example&quot; /&gt;&lt;/p&gt;
&lt;p&gt;From the image we can know that $K$ in problem statement can take any integer from $1$ to
$$
\left\lfloor\frac{z[i]}{i}\right\rfloor + 1
$$&lt;/p&gt;
&lt;p&gt;$i$ is the length of cyclic section. You can understand this expression by the example image. Red part equal to Orange part and Orange part equal to Orange part. Similarly find Red, Orange and Green part are all equal so $3$ is the length of cyclic section.&lt;/p&gt;
&lt;p&gt;Then we need focus on the number of each letter appear. Classify and discuss the parity of $K$. Define function $f(i,j)$ denote the number of letter wich appear odd times in $s[i\dots j]$. Let $t$ denote all value selection scheme for $K$, the scheme for $K$ to be a odd number should be $todd = t - t/2$, for even number should be $teven = t/2$.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;When $K$ is an odd, we should find the number of $j\ (j\le i)$ which satisfy $f(0,j) \le f(i,n-1)$ is denoted as $t1$, it will provide $todd \times t1$ contribution.
&lt;img src=&quot;https://cdn.luogu.com.cn/upload/image_hosting/iv72qpb3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When $K$ is an even, we should find the number of $j\ (j\le i)$ which satisfy $f(0,j) \le f(0,n-1)$ is denoted as $t2$, that is because the number of cyclic section is even, even if the letter appear for odd times, it finally turned to even times. So the letter which appear odd time equal to whole string. Answer will increase $teven \times t2$.
&lt;img src=&quot;https://cdn.luogu.com.cn/upload/image_hosting/6pxq01fv.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;LuoguP3167 [CQOI2014] 通配符匹配&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3167&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/e4th1mfd&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;OK this problem can protrude the charm of DFS. Most of turorial are using difficult algorithm such as KMP, ACAM but forget the most easy algorithm.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
char ch[100001],wzc[100001];
int n;

bool _doudou(int x,int y)// Start brute-force search, search from back to front to avoid some tricky test cases, also makes implementation easier
{
    if(y==0)// If the pattern string is fully matched
    {
        if(x==0)return 1;// If the wildcard string is also fully consumed, then it&apos;s definitely correct
        for(int i=x;i&amp;gt;0;i--)// Check remaining &apos;*&apos; characters
            if(ch[i]!=&apos;*&apos;)return 0;// If anything other than &apos;*&apos;, then it fails
        return 1;// Otherwise it&apos;s fine
    }

    if(!x)return 0;// If the wildcard string finishes first, then it must fail

    if(ch[x]==&apos;*&apos;)// If we encounter a &apos;*&apos;
    {
        for(int i=y;i&amp;gt;=0;i--)// Try matching from all remaining positions.
                             // The time complexity looks high, but in practice most branches terminate quickly.
            if(_doudou(x-1,i))return 1;
    }
    else
    {
        if(wzc[y]==ch[x]||ch[x]==&apos;?&apos;)
            return _doudou(x-1,y-1);// If it&apos;s &apos;?&apos;, move both strings one position forward
        else return 0;// Match failed
    }
}

int main()
{
    scanf(&quot;%s%d&quot;,ch+1,&amp;amp;n);
    int len=strlen(ch+1);

    while(n--)
    {
        scanf(&quot;%s&quot;,wzc+1);

        if(_doudou(len,strlen(wzc+1)))
            printf(&quot;YES\n&quot;);
        else
            printf(&quot;NO\n&quot;);
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;LuoguP3082 [USACO13MAR] Necklace G&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Useful Link&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/problem/P3082&quot;&gt;Problem Statement&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.luogu.com.cn/article/vhpibjbr&quot;&gt;Reference Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a problem which using KMP to optmize DP.&lt;/p&gt;
&lt;p&gt;Let $f_{i,j}$ denotes, the maximum we can keep when the first $i$-th letter of $a$ map the first $j$-th letter of $b$ exactly. Then we can get a easy transition&lt;/p&gt;
&lt;p&gt;$$
f_{i+1,k} = \max{f_{i+1,k} ,f_{i,j}+1}
$$&lt;/p&gt;
&lt;p&gt;Then we should consider how to calculate $k$ quickly. Let $g_{i,j}$ denotes $k$ when map the first $i$-th letter of $b$ exactly and then next letter is $j$. Then we can get below transition.&lt;/p&gt;
&lt;p&gt;$$
g_{i,j} =
\begin{cases}
i+1 &amp;amp; b[i+1] == j\
g_{nxt_{i}, j} &amp;amp; \operatorname{otherwise.}
\end{cases}
$$&lt;/p&gt;
&lt;p&gt;Coding is easy.&lt;/p&gt;
</content:encoded></item><item><title>Memo of Trigonometric Formula</title><link>https://blog.517group.cn/posts/202603222001/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202603222001/</guid><description>Useful</description><pubDate>Sun, 22 Mar 2026 20:01:23 GMT</pubDate><content:encoded>&lt;p&gt;This is a memo. To prevent me from forgetting some trigonometric formulas.&lt;/p&gt;
&lt;h1&gt;Basic Formula&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Unit circle&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pythagorean Theotheorm&lt;/p&gt;
&lt;p&gt;$$
\cos^2 \theta + \sin^2 \theta = 1
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rotate matrix&lt;/strong&gt; (counterclockwise rotation)&lt;/p&gt;
&lt;p&gt;$$
\left [
\begin{matrix}
\cos\theta &amp;amp; -\sin\theta \
\sin\theta &amp;amp; \cos\theta
\end{matrix}
\right ]
\times
\left [
\begin{matrix}
x \ y
\end{matrix}
\right ]
$$&lt;/p&gt;
&lt;h1&gt;Sum Formula&lt;/h1&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;amp; \sin(\alpha + \beta) = \sin\alpha\cos\beta + \cos\alpha\sin\beta\
&amp;amp; \cos(\alpha + \beta) = \cos\alpha\cos\beta - \sin\alpha\sin\beta\
&amp;amp; \tan(\alpha + \beta) = \frac{\tan\alpha + \tan\beta}{1-\tan\alpha\tan\beta}
\end{aligned}
$$&lt;/p&gt;
&lt;h1&gt;Double and Half Formula&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Double-angle&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For sin.&lt;/p&gt;
&lt;p&gt;$$
\sin 2\theta = 2\sin\theta\cos\theta
$$&lt;/p&gt;
&lt;p&gt;For cos.&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\cos 2\theta
&amp;amp;= \cos^2\theta - \sin^2\theta \
&amp;amp;= 1 - 2\sin^2\theta\
&amp;amp;= 2\cos^2\theta -1
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;For tan.&lt;/p&gt;
&lt;p&gt;$$
\tan 2\theta = \frac{2\tan\theta}{1-\tan^2\theta}
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Half-angle&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For sin &amp;amp; cos.&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\sin \frac{\theta}{2} &amp;amp;= \pm \sqrt{\frac{1-\cos\theta}{2}} \
\cos \frac{\theta}{2} &amp;amp;= \pm \sqrt{\frac{1+\cos\theta}{2}}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;For tan.&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\tan \frac{\theta}{2}
&amp;amp;= \frac{\sin\theta}{1 + \cos\theta}\
&amp;amp;= \frac{1 - \cos\theta}{\sin\theta}\
&amp;amp;= \pm \sqrt{\frac{1-\cos\theta}{1+\cos\theta}}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;The sign is depend on which quardrant $\theta/2$  is in.&lt;/p&gt;
&lt;p&gt;Also, there are some other trigonometric identities but they all can be prove by above formula so it&apos;s all for this memo.&lt;/p&gt;
</content:encoded></item><item><title>Mobius Inversion</title><link>https://blog.517group.cn/posts/202602091042/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202602091042/</guid><description>Basic number theory knowledge</description><pubDate>Mon, 09 Feb 2026 10:42:13 GMT</pubDate><content:encoded>&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Inversion is a common tool to solve some math problem.&lt;/p&gt;
&lt;p&gt;This passage will introduce some trick for solving these problem.&lt;/p&gt;
&lt;h1&gt;Prerequisites&lt;/h1&gt;
&lt;h2&gt;Multiplicative Functions&lt;/h2&gt;
&lt;p&gt;A function $f(n)$ is multiplicative if, for all $a,b$ with $\gcd(a,b)=1$,
it satisfies $f(ab)=f(a)f(b)$. In particular, if this holds for all $a,b$,
then the function is called completely multiplicative.&lt;/p&gt;
&lt;p&gt;Common multiplicative functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identity function: $\epsilon(n) = [n=1]$, complete&lt;/li&gt;
&lt;li&gt;Constant function: $\operatorname{1}(n) = 1$, complete&lt;/li&gt;
&lt;li&gt;Equality function: $\operatorname{id}(n) = n$, $\operatorname{id}_{k}(n)=n^k$, complete&lt;/li&gt;
&lt;li&gt;Euler function: $\varphi(n) = \sum_{i=1}^{n}[\operatorname{gcd}(i,n)=1]$&lt;/li&gt;
&lt;li&gt;Mobius function:
$$
\mu(n)=\begin{cases}
1 &amp;amp; n=1\
0 &amp;amp; \exists\ d\ \operatorname{satisfy\ that}\ d^2 | n\
(-1)^k &amp;amp; k\ \operatorname{denote\ quantity\ of\ distinct\ prime\ divisors}
\end{cases}
$$&lt;/li&gt;
&lt;li&gt;Number of divisors function: $\operatorname{d}(n) = \sum_{d|n}1$&lt;/li&gt;
&lt;li&gt;Sum of divisors function: $\sigma(n) = \sum_{d|n}d$&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Dirichlet Convolution&lt;/h2&gt;
&lt;p&gt;Below is the formula of Dirichlet convolution:
$$
(f\ast g)(n) = \sum_{d|n} f(d)g(\frac{n}{d})
$$&lt;/p&gt;
&lt;p&gt;Dirichlet convolution satisfy below law:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Commutative law : $f\ast g = g\ast f$;&lt;/li&gt;
&lt;li&gt;Associative law : $(f\ast g)\ast h = f\ast (g\ast h)$;&lt;/li&gt;
&lt;li&gt;Distributive law : $f\ast (g+h) = f\ast g + f\ast h$;&lt;/li&gt;
&lt;li&gt;Identity element : $f\ast\epsilon = f$.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And below is some important dirichlet convolution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\epsilon =\mu\ast 1$&lt;/li&gt;
&lt;li&gt;$\operatorname{id} = \varphi\ast 1$&lt;/li&gt;
&lt;li&gt;$\operatorname{d}=1\ast 1$&lt;/li&gt;
&lt;li&gt;$\sigma = \operatorname{id}\ast 1$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The proof of these equation isn&apos;t difficult but very important, must try to prove them before reading below part.&lt;/p&gt;
&lt;h1&gt;Mobius Inversion&lt;/h1&gt;
&lt;p&gt;The following is the basic form of mobius inversion:&lt;/p&gt;
&lt;p&gt;If
$$
F(n) = \sum_{d|n}f(d),
$$
then
$$
f(n) = \sum_{d|n}\mu(d)F\left(\frac{n}{d}\right).
$$&lt;/p&gt;
&lt;p&gt;These relative explain &quot;Inversion&quot; perfectly: using Mobius function to get calculation method from $F(n)$ to $f(n)$ by the calculation method from $f(n)$ to $F(n)$.&lt;/p&gt;
&lt;p&gt;And we can using below formula to prove these parttern easily.&lt;/p&gt;
&lt;p&gt;Observe the form of the relation from $f(d)$ we can using Dirichlet Convolution to express it:
$$
\begin{aligned}
f &amp;amp;= f \ast \epsilon\
&amp;amp;= f \ast \mu \ast 1 \
&amp;amp;= f \ast 1 \ast \mu \
&amp;amp;= F \ast \mu \
&amp;amp;= \mu \ast F \
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;I will explain each step of this proof.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Dirichelet Convolution&apos;s law: identity element $\epsilon$&lt;/li&gt;
&lt;li&gt;$\epsilon = \mu \ast 1$&lt;/li&gt;
&lt;li&gt;Commutative law&lt;/li&gt;
&lt;li&gt;The relative from $f(n)$ to $F(n)$ : $F = f \ast 1$&lt;/li&gt;
&lt;li&gt;Commutative law&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Mobius Inversion also have another form, this form is base on multiple.&lt;/p&gt;
&lt;p&gt;If&lt;/p&gt;
&lt;p&gt;$$
F(n)=\sum_{n|d}f(d),
$$&lt;/p&gt;
&lt;p&gt;then&lt;/p&gt;
&lt;p&gt;$$
f(n)=\sum_{n|d}\mu\left(\frac{d}{n}\right)F(d).
$$&lt;/p&gt;
&lt;p&gt;This form cannot prove elegantly like before.&lt;/p&gt;
&lt;p&gt;We can expand $F(d)$ in the formula after inversion&lt;/p&gt;
&lt;p&gt;$$
f(n)=\sum_{n|d}\mu\left(\frac{d}{n}\right)\sum_{d|e}f(e).
$$&lt;/p&gt;
&lt;p&gt;Then swap summation:&lt;/p&gt;
&lt;p&gt;$$
f(n) = \sum_{n|e} f(e) \sum_{n | d | e} \mu\left(\frac{d}{n}\right).
$$&lt;/p&gt;
&lt;p&gt;Then we define $d = nt, e = ns$ then this equation change to:&lt;/p&gt;
&lt;p&gt;$$
f(n) = \sum_{s\ge 1} f(ns)\sum_{t|s}\mu(t)
$$&lt;/p&gt;
&lt;p&gt;As we know, because of the natrue of mobius function:&lt;/p&gt;
&lt;p&gt;$$
\sum_{t|s}\mu(t) =
\begin{cases}
1 &amp;amp; s = 1\
0 &amp;amp; s &amp;gt; 1
\end{cases}
$$&lt;/p&gt;
&lt;p&gt;So that equation just can get result when $s = 1$, so the equation change to $f(n) = f(n)$, we finish the proof of multiple form of mobius inversion.&lt;/p&gt;
&lt;h1&gt;Classic Problem&lt;/h1&gt;
&lt;p&gt;Before solve some question, we need know a core trick.&lt;/p&gt;
&lt;p&gt;$$
[\gcd(i,j)=1]=\sum_{d|\gcd(i,j)}\mu(d)
$$&lt;/p&gt;
&lt;p&gt;This trick even no need to prove. I already say mobius function have a natrue that  the sum of the Mobius functions of the divisors of a number equal to 1 only when that number equal to 1.&lt;/p&gt;
&lt;h2&gt;Problem 1&lt;/h2&gt;
&lt;p&gt;$$
\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j) = k]
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j) = k]
&amp;amp;=\sum_{i=1}^{\lfloor n/k\rfloor}\sum_{j=1}^{\lfloor m/k\rfloor}[\gcd(i,j)=1]\
&amp;amp;=\sum_{i=1}^{\lfloor n/k\rfloor}\sum_{j=1}^{\lfloor m/k\rfloor}\sum_{d|\gcd(i,j)}\mu(d)\
&amp;amp;=\sum_{d=1}^{\min(\lfloor n/k\rfloor,\lfloor m/k\rfloor)}\mu(d)\sum_{i=1}^{\lfloor n/k\rfloor}[d|i]\sum_{j=1}^{\lfloor m/k\rfloor}[d|j]\
&amp;amp;=\sum_{d=1}^{\min(\lfloor n/k\rfloor,\lfloor m/k\rfloor)}\mu(d)\left\lfloor\frac{n}{kd}\right\rfloor\left\lfloor\frac{m}{kd}\right\rfloor
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Problem 2&lt;/h2&gt;
&lt;p&gt;$$
\sum_{i=1}^n\sum_{j=1}^md(ij)
$$&lt;/p&gt;
&lt;p&gt;Function $d(n)$ is number of divisors function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core Lemma:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$
d(ij)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\sum_{i=1}^n\sum_{j=1}^md(ij)
&amp;amp;=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]\
&amp;amp;=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}\sum_{d|\gcd(x,y)}\mu(d)\
&amp;amp;=\sum_{x=1}^n\sum_{y=1}^m\left\lfloor\frac{n}{x}\right\rfloor\left\lfloor\frac{m}{y}\right\rfloor\sum_{d|\gcd(x,y)}\mu(d)\
&amp;amp;=\sum_{d=1}^{\min(n, m)}\mu(d)\sum_{x=1}^n\sum_{y=1}^m\left\lfloor\frac{n}{x}\right\rfloor\left\lfloor\frac{m}{y}\right\rfloor[d|x][d|y]\
&amp;amp;=\sum_{d=1}^{\min(n, m)}\mu(d)\sum_{x=1}^n\left\lfloor\frac{n}{xd}\right\rfloor\sum_{y=1}^m\left\lfloor\frac{m}{yd}\right\rfloor
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Problem 3&lt;/h2&gt;
&lt;p&gt;$$
\sum_{i=1}^n\sum_{j=1}^n i\times j\times \gcd(i,j)
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\sum_{i=1}^n\sum_{j=1}^n i\times j\times \gcd(i,j)
&amp;amp;=\sum_{d=1}^nd\sum_{i=1}^n\sum_{j=1}^nij[\gcd(i,j)=1]\
&amp;amp;=\sum_{d=1}^nd^3\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor n/d\rfloor}ij[\gcd(i,j)=1]\
&amp;amp;=\sum_{d=1}^nd^3\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor n/d\rfloor}ij\sum_{k|\gcd(i,j)}\mu(k)\
&amp;amp;=\sum_{d=1}^nd^3\sum_{k=1}^{\lfloor n/d\rfloor}\mu(k)\sum_{i&apos;=1}^{\lfloor n/(kd)\rfloor}(ki&apos;)\sum_{j&apos;=1}^{\lfloor n/(kd)\rfloor}(kj&apos;)\
&amp;amp;=\sum_{d=1}^nd^3\sum_{k=1}^{\lfloor n/d\rfloor}\mu(k)k^2\sum_{i&apos;=1}^{\lfloor n/(kd)\rfloor}i&apos;\sum_{j&apos;=1}^{\lfloor n/(kd)\rfloor}j&apos;\
&amp;amp;=\sum_{d=1}^nd^3\sum_{k=1}^{\lfloor n/d\rfloor}\mu(k)k^2\sum_{i&apos;=1}^{\lfloor n/(kd)\rfloor}i&apos;\sum_{j&apos;=1}^{\lfloor n/(kd)\rfloor}j&apos;\
&amp;amp;=\sum_{d=1}^nd^3\sum_{k=1}^{\lfloor n/d\rfloor}\mu(k)k^2S\left(\left\lfloor\frac{n}{kd}\right\rfloor\right)^2
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Problem 4&lt;/h2&gt;
&lt;p&gt;$$
\prod_{i=1}^n\prod_{j=1}^mf_{\gcd(i,j)}
$$&lt;/p&gt;
&lt;p&gt;Function $f(n)$ is Fibonacci sequence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core Lemma:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$
f_{\gcd(i,j)}=\gcd(f_i, f_j)
$$&lt;/p&gt;
&lt;p&gt;Below is proof.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lemma 1.&lt;/strong&gt; $\gcd(f_n,f_{n-1})=1$&lt;/p&gt;
&lt;p&gt;Prove it by mathematical induction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Base case:
&lt;ul&gt;
&lt;li&gt;For $n=1$, $\gcd(f_1, f_0) = \gcd(1, 0) = 1$&lt;/li&gt;
&lt;li&gt;For $n=2$, $\gcd(f_2, f_1) = \gcd(1, 1) = 1$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Inductive hypothesis: Assume it holds for $n = k$.&lt;/li&gt;
&lt;li&gt;Inductive step: When $n = k+1$, $f_{k+1} = f_k + f_{k-1}$, so $\gcd(f_{k+1},f_k)=\gcd(f_k+f_{k-1},f_k)=\gcd(f_k,f_{k-1})=1$. (Becasue of $\gcd(x+y,y) = \gcd(x,y)$)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Lemma 2.&lt;/strong&gt; When $m &amp;gt; n$, $\gcd(f_m,f_n)=\gcd(f_n, f_{m\bmod n})$ holds.&lt;/p&gt;
&lt;p&gt;Before prove this lemma, we should know a law $f_{a+b}=f_{a+1}f_b + f_af_{b-1}$
Still use mathematical induction to prove it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Base case: For $b = 1$, $f_{a+1}=f_{a+1}f_1 + f_af_0 = f_{a+1}$&lt;/li&gt;
&lt;li&gt;Inductive hypothesis: Assume it holds when $b \le k$.&lt;/li&gt;
&lt;li&gt;Inductive step: When $b = k+1$,
$$
\begin{aligned}
f_{a+k+1}
&amp;amp;=f_{a+k} + f_{a+k-1}\
&amp;amp;=(f_{a+1}f_k + f_af_{k-1}) + (f_{a+1}f_{k-1} + f_af_{k-2})\
&amp;amp;=f_{a+1}(f_k+f_{k-1}) + f_{a}(f_{k-1}+f_{k-2})\
&amp;amp;=f_{a+1}f_{k+1}+f_{a}f_{k}.
\end{aligned}
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When try to prove Lemma 2.&lt;/p&gt;
&lt;p&gt;Let $a=n,b=(q-1)n+r$, then $f_m = f_{qn+r} = f_{n+[(q-1)n+r]} = f_{n+1}f_{(q-1)n+r}+f_{n}f_{(q-1)n+r-1}$, So:
$$
\gcd(f_m, f_n) = \gcd(f_{n+1}f_{(q-1)n+r}+f_nf_{(q-1)n+r-1}, f_n).
$$&lt;/p&gt;
&lt;p&gt;Because of $\gcd(x+ky, y) = \gcd(x, y)$, so we can simplify above expression to below one:&lt;/p&gt;
&lt;p&gt;$$
\gcd(f_m, f_n) = \gcd(f_{n+1}f_{(q-1)n+r}, f_n).
$$&lt;/p&gt;
&lt;p&gt;Then becasue of Lemma 1, we can know that $\gcd(f_m, f_n) = \gcd(f_{(q-1)n+r}, f_n)$ (because there are no common divisior between $f_{n+1}$ and $f_n$), then repeat this process, we can found taht $\gcd(f_m, f_n) = \gcd(f_{m\bmod n}, f_n)$&lt;/p&gt;
&lt;p&gt;Now we going to prove that core lemma.&lt;/p&gt;
&lt;p&gt;Let $d = \gcd(i,j)$. By the Euclidean algorithm,
$$
\gcd(i, j) = \gcd(j, i \bmod j) = \gcd(i \bmod j, j \bmod (i \bmod j)) = \dots = \gcd(d, 0) = d.
$$&lt;/p&gt;
&lt;p&gt;Combining &lt;strong&gt;Lemma 2&lt;/strong&gt;, we can apply the same reduction process to $\gcd(f_i, f_j)$:
$$
\gcd(f_i, f_j) = \gcd(f_j, f_{i \bmod j}) = \gcd(f_{i \bmod j}, f_{j \bmod (i \bmod j)}) = \dots = \gcd(f_d, f_0).
$$&lt;/p&gt;
&lt;p&gt;By definition, $f_0 = 0$, and $\gcd(f_d, 0) = f_d$ (the greatest common divisor of a non-zero number and $0$ is the number itself). Therefore,
$$
\gcd(f_i, f_j) = f_d = f_{\gcd(i,j)}.
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\prod_{i=1}^n\prod_{j=1}^mf_{\gcd(i,j)}
&amp;amp;=\prod_{d=1}^{\min(n, m)}{f_d}^{\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=d]}\
&amp;amp;=\prod_{d=1}^{\min(n, m)}{f_d}^{\sum_{d|T}^{\lfloor n/k\rfloor}\mu(k)\lfloor\frac{n}{dk}\rfloor\lfloor\frac{m}{dk}\rfloor}\
&amp;amp;=\prod_{d=1}^{\min(n, m)}\prod_{d|T}{f_d}^{\mu(T/d)\lfloor n/T\rfloor\lfloor m/T\rfloor}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;The last step is because $a^{x+y} = a^xa^y$, we dismantle the sum.&lt;/p&gt;
&lt;h1&gt;Summarize&lt;/h1&gt;
&lt;p&gt;The core idea of mobius inversion is optmize the time complexity of a expression by the natrue of mobius function.&lt;/p&gt;
</content:encoded></item><item><title>Virtual Tree</title><link>https://blog.517group.cn/posts/202602041847/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202602041847/</guid><description>A way to optmize some algorithm which time complexity is depend on tree size.</description><pubDate>Fri, 06 Feb 2026 11:54:11 GMT</pubDate><content:encoded>&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;As we know that there are some problem we need maintain some information on a sparse tree (When we describe a tree is &quot;sparse&quot;, there are few key points).&lt;/p&gt;
&lt;p&gt;Using this structure, some tree DP algorithms that depend on tree size can be proven correct.&lt;/p&gt;
&lt;h1&gt;Build Virtual Tree&lt;/h1&gt;
&lt;p&gt;We have two ways to build a tree.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;sort twice.&lt;/li&gt;
&lt;li&gt;monotonicity stack.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Sort Twice&lt;/h2&gt;
&lt;p&gt;This algrithm is easy to code but hard to understand, below is algorithm flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;sort key node by dfn.&lt;/li&gt;
&lt;li&gt;get LCA for each close node.&lt;/li&gt;
&lt;li&gt;build virtual tree.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;How to prove this algorithm?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If $x$ is an ancestor of $y$, then connect $x$ directly to $y$. Since the DFS order guarantees that the DFS orders of $x$ and $y$ are adjacent, there are no critical points on the path from $x$ to $y$.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If $x$ is not an ancestor of $y$, then consider $\operatorname{LCA}(x,y)$ as an ancestor of $y$. Based on the previous case, it can be proven that there are no critical points on the path from $\operatorname{LCA}(x,y)$ to $y$.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Therefore, connecting $\operatorname{LCA}(x,y)$ and $y$ will not result in any omissions or repetitions.&lt;/p&gt;
&lt;p&gt;Furthermore, will the fact that the first node is not connected to any node have any impact? Since the first node is always the root of the tree, it will not have any impact, so the total number of edges is $m-1$.&lt;/p&gt;
&lt;h2&gt;Monotonicity Stack&lt;/h2&gt;
&lt;p&gt;This algorithm is maintaining a right chain of virtual tree.&lt;/p&gt;
&lt;p&gt;Algorithm Flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;push node $1$ (root) in stack.&lt;/li&gt;
&lt;li&gt;sort the key node by dfs order.&lt;/li&gt;
&lt;li&gt;check the LCA of stack top and now node.&lt;/li&gt;
&lt;li&gt;pop stack top until dfs order of top is not greater than dfs order of LCA(top, now). we need add edge from new top to old top.&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;dfn[top] == dfn[LCA(top, now)]&lt;/code&gt;, LCA is already in stack add edge directly; if less than, add edge from LCA to old top, and push LCA in stack.&lt;/li&gt;
&lt;li&gt;then repeat above process.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;bool cmp(const int x, const int y) { return id[x] &amp;lt; id[y]; }

void build() {
    sort(h + 1, h + k + 1, cmp);
    sta[top = 1] = 1, g.sz = 0, g.head[1] = -1;
    // Push node 1 onto the stack, clear the adjacency list corresponding to node 1, and set the number of edges in the adjacency list to 0
    for (int i = 1, l; i &amp;lt;= k; ++i) {
        if (h[i] != 1) {
            // If node 1 is a key node, do not add it again
            l = lca(h[i], sta[top]);
            // Calculate the LCA between the current node and the top node of the stack
            if (l != sta[top]) {
                // If the LCA is different from the top element of the stack, it means that the current node is not on the chain stored in the current stack
                while (id[l] &amp;lt; id[sta[top - 1]]) {
                    // When the DFS order of the second largest node is greater than the DFS order of the LCA
                    g.push(sta[top - 1], sta[top]), top--;
                    // Connect the chains that do not overlap with the chain containing the current node and pop them
                }
                if (id[l] &amp;gt; id[sta[top - 1]]) {
                    // If the LCA is not equal to the second largest node (greater than or not equal to is essentially the same)
                    g.head[l] = -1, g.push(l, sta[top]), sta[top] = l;
                    // This indicates that the LCA is being pushed onto the stack for the first time. Clear its adjacency list, connect the edges, pop the top element from the stack, and push the LCA
                    // onto the stack
                } else {
                    g.push(l, sta[top--]);
                    // This indicates that the LCA is the second largest node. Pop the top element from the stack directly
                }
            }
            g.head[h[i]] = -1, sta[++top] = h[i];
            // The current node is necessarily the first one pushed onto the stack, clear the adjacency list and push it onto the stack
        }
    }
    for (int i = 1; i &amp;lt; top; ++i) {
        g.push(sta[i], sta[i + 1]); // Connect the last remaining chain
    }
    return ;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Summarize&lt;/h1&gt;
&lt;p&gt;Tricky. Aslo can be considered violence.&lt;/p&gt;
</content:encoded></item><item><title>Solution Report of Construct Topic</title><link>https://blog.517group.cn/posts/20260122/</link><guid isPermaLink="true">https://blog.517group.cn/posts/20260122/</guid><description>Trick problem</description><pubDate>Thu, 22 Jan 2026 15:54:20 GMT</pubDate><content:encoded>&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;It&apos;s the problem list of 01/22/2026 class, about construct problem, that&apos;s a tricky part in OI, know we will see some.&lt;/p&gt;
&lt;h1&gt;A - QOJ-4913 子集匹配&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://vjudge.net/problem/QOJ-4913/origin&quot;&gt;Problem Statement&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Transfer problem statement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$L$: All subset which exactly have $K$ ones.&lt;br /&gt;
$R$: All subset which exactly have $K-1$ ones.&lt;br /&gt;
One edge from $S$ to $T$ equal to change a 1-node in $S$ to 0 then $S$ equal to $T$.&lt;br /&gt;
This problem require different $S$ cannot contact same $T$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Construct Idea:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;let 1 equal to add 1 and 0 equal to minus 1.&lt;br /&gt;
then find the max position $p$ of prefix sum.&lt;br /&gt;
flip 0&amp;amp;1 in position $p+1$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Prove: Why it is a injection?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;assumpted there is a $S&apos;$ mapped same $T$ with $S$.&lt;br /&gt;
we can find that $T$ have $i,j$ places which $S$ or $S&apos;$ is $1$ but now is $0$, so $T$ must have $K-2$ ones, it&apos;s not satisfy difinition of $T$, so this mapping must be a injection.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Good problem, it&apos;s take me 1 day to understand.&lt;/p&gt;
&lt;h1&gt;B - Adjacent Difference&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://atcoder.jp/contests/agc066/tasks/agc066_a?lang=en&quot;&gt;Problem Statement&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For this kind of construction problem, we can try to solve it using some special subtask.&lt;/p&gt;
&lt;p&gt;If this matrix just have 0/1, and $d$ equal to $1$, how to modify it? It&apos;s not difficult to find that answer must be below form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;010  |  101
101  |  010
010  |  101
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can calculate answer in each situation, get the mininum. Then try to expand this special solution to whole problem. We can enumerate a $k$ then difine odd and even be below rules:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\operatorname{odd}\ &amp;amp;: \ a \equiv k &amp;amp; \pmod{2d}\
\operatorname{even}\ &amp;amp;: \ a \equiv k+d &amp;amp; \pmod{2d}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;Using above solution, time complexity is $O(dn^2)$.&lt;/p&gt;
&lt;h1&gt;C - Make SYSU Great Again II&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://qoj.ac/problem/7629&quot;&gt;Problem Statement&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We call the cell which:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$(i+j)\bmod 2 = 0$ is &lt;strong&gt;Black Cell&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;$(i+j)\bmod 2 = 1$ is &lt;strong&gt;White Cell&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we need promise the bitwise and value of each close Black and White cell equal to 0. Then we can start our constructing.&lt;/p&gt;
&lt;p&gt;First we have below guess:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Assign unique number in Black Cell.&lt;/li&gt;
&lt;li&gt;Then put avaliable number in White Cell.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We need find a way to calculate the number in cell quickly.&lt;/p&gt;
&lt;p&gt;For black cell:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;high bits = gray( (i + j) / 2 )
low  bits = gray( (i - j + (n-1)) / 2 )

value = (high &amp;lt;&amp;lt; K) | low
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can proove that different cell $(i,j)$ map different gray code number.&lt;/p&gt;
&lt;p&gt;Maybe four neighbors can generate at most 4 same number, and add one of black cell, at most 5 same number, statisfy the problem requirement.&lt;/p&gt;
&lt;h1&gt;D - Tournament Construction&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/850/D&quot;&gt;Problem Statement&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;According to the Landau theorem, we sort the original sequence $a$ and define $f(i,j,y)$ as representing a graph of size $i$ with vertices $j$ and edges $y$.&lt;/p&gt;
&lt;p&gt;According to the theorem, $y ≥ \frac{j(j−1)}{2}$ always holds true.&lt;/p&gt;
&lt;p&gt;We enumerate $i,j,y$ and the state at $i−1$. If $f(i−1,k,x)$ is feasible, then $f(i,j,y)$ is also feasible.&lt;/p&gt;
&lt;p&gt;We also record $j−k$ at this point.&lt;/p&gt;
&lt;p&gt;This allows us to construct the original out-degree sequence.&lt;/p&gt;
&lt;p&gt;Let the out-degree sequence established in step 1 be $d_i$, and the one established in step 2 be $u_i$. First, assume $\forall i&amp;gt;j$, and all edges are in the direction $i\rightarrow j$. Then $u_i = i−1$.&lt;/p&gt;
&lt;p&gt;Each time, find a triple $(i,j,k)$ such that $u_i &amp;gt; d_i, u_j = d_j, u_k &amp;lt; d_k$, and there exist edges $i\rightarrow j$ and $j\rightarrow k$.&lt;/p&gt;
&lt;p&gt;In this way, we can reverse these two edges, achieving the effect that $u_i \leftarrow u_i −1, u_j \leftarrow u_j, u_k \leftarrow u_k −1$.&lt;/p&gt;
&lt;p&gt;By continuously repeating the above steps, u can gradually approach d, eventually becoming exactly the same.&lt;/p&gt;
&lt;h1&gt;E - LGP10441 [JOIST 2024] 乒乓球 / Table Tennis&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P10441&quot;&gt;Problem Statememt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We have a classic conclusion: the struction of tournament just depend on in-degree array, and triple circle will add one more when we change a pair $(x,x+2)$ to $(x+1,x+1)$.&lt;/p&gt;
&lt;p&gt;So we can use this to construct it.&lt;/p&gt;
&lt;p&gt;Find the smallest $n_0$ which can statisfy the maximum triple circle less than $m$, then change the triple circle quantity on it.&lt;/p&gt;
</content:encoded></item><item><title>Fast Mobius Transform and Fast Walsh-Hadamard Transform</title><link>https://blog.517group.cn/posts/202601162039/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202601162039/</guid><description>A basic transform to calculate convolution</description><pubDate>Thu, 22 Jan 2026 10:39:18 GMT</pubDate><content:encoded>&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;These two transforms have a lot of same point, so I want to introduce them together.&lt;/p&gt;
&lt;p&gt;First of all, what&apos;s kind of problem these algorithm solve? It&apos;s used to solve formula like below format:&lt;/p&gt;
&lt;p&gt;$$
c_k = \sum_{i\oplus j = k} a_i\times b_j
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\oplus$ can be any binary bitwise operations such as &lt;code&gt;or&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt;, &lt;code&gt;xor&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And FMT(Fast Mobius Transform) is used to solve above formula when operation is &lt;code&gt;or&lt;/code&gt; or &lt;code&gt;and&lt;/code&gt;, FWT(Fast Walsh-Hadamard Transform) is used when operation is &lt;code&gt;xor&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These two algorithms are too similar so that maybe you can see some blog or solution said they are same algorithm but please remember they not.&lt;/p&gt;
&lt;h1&gt;FMT&lt;/h1&gt;
&lt;p&gt;Let&apos;s start with operation &lt;code&gt;or&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The algorithm flow is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find a transform to transfer array $a, b$, let the array transfered named $A,B$;&lt;/li&gt;
&lt;li&gt;Define $C$ such that $C_i = A_i\times B_i$;&lt;/li&gt;
&lt;li&gt;Use the inverse transform to get $c$ from $C$.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Or Operation&lt;/h2&gt;
&lt;p&gt;now the formula is:
$$
c_k = \sum_{i\lor j = k} a_i\times b_j
$$&lt;/p&gt;
&lt;p&gt;We need construct a kind of transform by the algorithm flow. Let
$$
A_i = \sum_{i=i\cup j} a_j
$$&lt;/p&gt;
&lt;p&gt;And we can try derive it:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
A_i\times B_i
&amp;amp;= \left(\sum_{i\cup j=i}a_j\right)\left(\sum_{i\cup k=i}b_k\right)\
&amp;amp;= \sum_{i\cup(j\cup k) = i}a_jb_k\
&amp;amp;= C_i
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;This form can use a inverse transform to get $c$ form $C$.&lt;/p&gt;
&lt;p&gt;Now try find a quick way to calculate this transform. We know $i=i\cup j$ equal to find all the subset $j$ of $i$, and this need $O(3^n)$ time complexity, too slow.&lt;/p&gt;
&lt;p&gt;Maybe we can focus on index:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;origin index&lt;/th&gt;
&lt;th&gt;0&lt;/th&gt;
&lt;th&gt;1&lt;/th&gt;
&lt;th&gt;2&lt;/th&gt;
&lt;th&gt;3&lt;/th&gt;
&lt;th&gt;4&lt;/th&gt;
&lt;th&gt;5&lt;/th&gt;
&lt;th&gt;6&lt;/th&gt;
&lt;th&gt;7&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;binary form&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;000&lt;/td&gt;
&lt;td&gt;001&lt;/td&gt;
&lt;td&gt;010&lt;/td&gt;
&lt;td&gt;011&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;110&lt;/td&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There is obviously a pattern: 0 and 4, 1 and 5, 2 and 6, etc. They have the same last two bits, and 0 is a subset of 4. This told us we can use this pattern make we calculate transform quickly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto FMTor(const std::vector&amp;lt;ll&amp;gt;&amp;amp; a, int flag) -&amp;gt; std::vector&amp;lt;ll&amp;gt; {
    auto trA{a};
    for (int o{2}, k{1}; o &amp;lt;= n; o &amp;lt;&amp;lt;= 1, k &amp;lt;&amp;lt;= 1) {
        for (int i{0}; i &amp;lt; n; i += o) {
            for (int j{0}; j &amp;lt; k; j++) {
                trA[i+j+k] = (trA[i+j+k] + trA[i+j] * flag % MOD + MOD) % MOD;
            }
        }
    }
    return trA;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The time complexity of this code optmize above transform to O(n\times 2^n), because the length of array is $2^n$, maybe we can consider this algorithm is $O(n\log n)$ time complexity.&lt;/p&gt;
&lt;h2&gt;And Operation&lt;/h2&gt;
&lt;p&gt;The algorithm flow is same.&lt;/p&gt;
&lt;p&gt;We are trying to find a transform like FMT Or operation.&lt;/p&gt;
&lt;p&gt;Let $A$ is the array after transform, we have:
$$
A_i = \sum_{i=i\cap j} a_j
$$&lt;/p&gt;
&lt;p&gt;That means $j$ is the superset of $i$, so just like before but we should add contribution from superset to subset.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto FMTand(const std::vector&amp;lt;ll&amp;gt;&amp;amp; a, int flag) -&amp;gt; std::vector&amp;lt;ll&amp;gt; {
    auto trA{a};
    for (int o{2}, k{1}; o &amp;lt;= n; o &amp;lt;&amp;lt;= 1, k &amp;lt;&amp;lt;= 1) {
        for (int i{0}; i &amp;lt; n; i += o) {
            for (int j{0}; j &amp;lt; k; j++) {
                trA[i+j] = (trA[i+j] + trA[i+j+k] * flag % MOD + MOD) % MOD;
            }
        }
    }
    return trA;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Full Template&lt;/h2&gt;
&lt;p&gt;Really not difficult&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * @file    : FastMobiusTransform.cpp 
 * @date    : 2026-01-15
 * @brief   : LuoguP4717
 */

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;

class FastMobiusTransform {
private:
    using ll = long long;

    const int MOD;
    int n;
    std::vector&amp;lt;ll&amp;gt; a, b;
public:
    explicit FastMobiusTransform(int n, int MOD) : MOD{MOD}, n{n}, a(n), b(n) {}
    FastMobiusTransform(int MOD, const std::vector&amp;lt;ll&amp;gt; a, const std::vector&amp;lt;ll&amp;gt; b)
        : MOD{MOD}, n(a.size()), a{a}, b{b} {}

    void input() {
        for (int i{0}; i &amp;lt; n; i++) {
            std::cin &amp;gt;&amp;gt; a[i];
        }
        for (int i{0}; i &amp;lt; n; i++) {
            std::cin &amp;gt;&amp;gt; b[i];
        }
    }

    auto FMTor(const std::vector&amp;lt;ll&amp;gt;&amp;amp; a, int flag) -&amp;gt; std::vector&amp;lt;ll&amp;gt; {
        auto trA{a};
        for (int o{2}, k{1}; o &amp;lt;= n; o &amp;lt;&amp;lt;= 1, k &amp;lt;&amp;lt;= 1) {
            for (int i{0}; i &amp;lt; n; i += o) {
                for (int j{0}; j &amp;lt; k; j++) {
                    trA[i+j+k] = (trA[i+j+k] + trA[i+j] * flag % MOD + MOD) % MOD;
                }
            }
        }
        return trA;
    }
    auto FMTand(const std::vector&amp;lt;ll&amp;gt;&amp;amp; a, int flag) -&amp;gt; std::vector&amp;lt;ll&amp;gt; {
        auto trA{a};
        for (int o{2}, k{1}; o &amp;lt;= n; o &amp;lt;&amp;lt;= 1, k &amp;lt;&amp;lt;= 1) {
            for (int i{0}; i &amp;lt; n; i += o) {
                for (int j{0}; j &amp;lt; k; j++) {
                    trA[i+j] = (trA[i+j] + trA[i+j+k] * flag % MOD + MOD) % MOD;
                }
            }
        }
        return trA;
    }

    auto transformOr() -&amp;gt; std::vector&amp;lt;ll&amp;gt; {
        auto trA = FMTor(a, 1);
        auto trB = FMTor(b, 1);
        std::vector&amp;lt;ll&amp;gt; trC(n);
        for (int i{0}; i &amp;lt; n; i++) {
            trC[i] = trA[i] * trB[i] % MOD;
        }
        return FMTor(trC, -1);
    }
    auto transformAnd() -&amp;gt; std::vector&amp;lt;ll&amp;gt; {
        auto trA = FMTand(a, 1);
        auto trB = FMTand(b, 1);
        std::vector&amp;lt;ll&amp;gt; trC(n);
        for (int i{0}; i &amp;lt; n; i++) {
            trC[i] = trA[i] * trB[i] % MOD;
        }
        return FMTand(trC, -1);
    }
};

auto main() -&amp;gt; int {
    int n, MOD;
    std::cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; MOD;
    FastMobiusTransform fmt(1&amp;lt;&amp;lt;n, MOD);
    fmt.input();

    auto ans_or{fmt.transformOr()};
    for (auto&amp;amp; p : ans_or) {
        std::cout &amp;lt;&amp;lt; p &amp;lt;&amp;lt; &apos; &apos;;
    }
    std::cout &amp;lt;&amp;lt; &quot;\n&quot;;
    auto ans_and{fmt.transformAnd()};
    for (auto&amp;amp; p : ans_and) {
        std::cout &amp;lt;&amp;lt; p &amp;lt;&amp;lt; &apos; &apos;;
    }
    std::cout &amp;lt;&amp;lt; &quot;\n&quot;;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;FWT&lt;/h1&gt;
&lt;p&gt;As you can see this part we are going to introduce FWT, this algorithm is using to solve below formula:
$$
c_k = \sum_{i\oplus j = k} a_i\times b_j
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\oplus$ indicates XOR operator.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let $A$ is the transformed array of $a$ that:
$$
A_i = \sum_{i\circ j = 0} a_j - \sum_{i\circ j = 1} a_j
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the formula, $i\circ j$ indicates $\operatorname{popcount}(i\cap j)\bmod 2$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And we can check its correctness:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
A_i\times B_i
&amp;amp;= \left(\sum_{i\circ j = 0} a_j - \sum_{i\circ j = 1} a_j\right)\times \left(\sum_{i\circ k = 0} b_k - \sum_{i\circ k = 1} b_k\right)\
&amp;amp;= \left(\sum_{i\circ j = 0} a_j\sum_{i\circ k = 0} b_k + \sum_{i\circ j = 1}a_j\sum_{i\circ k = 1} b_k\right) - \left(\sum_{i\circ j = 0} a_j\sum_{i\circ k = 1} b_k + \sum_{i\circ j = 1}a_j\sum_{i\circ k = 0} b_k\right)\
&amp;amp;= \sum_{(j\oplus k)\circ i = 0} a_ib_k - \sum_{(j\oplus k)\circ i = 1} a_ib_k\
&amp;amp;= C_i
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;The transformation between second row and third row is because each of they denote one situation of result of $i\oplus j$, so they mix to one part.&lt;/p&gt;
&lt;p&gt;How to calculate it? Still divide, we know that:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Do &quot;$\circ$&quot; operater&lt;/th&gt;
&lt;th&gt;0&lt;/th&gt;
&lt;th&gt;1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So that:
$$
{A&apos;}_0 = A_0+A_1\
{A&apos;}_1 = A_0-A_1
$$&lt;/p&gt;
&lt;p&gt;Also we can get inverse transform:
$$
A_0 = \frac{A&apos;_0+A&apos;_1}{2}\
A_1 = \frac{A&apos;_0-A&apos;_1}{2}
$$&lt;/p&gt;
&lt;p&gt;Code also easily.&lt;/p&gt;
&lt;h2&gt;Full Template&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/**
 * @file    : FastWalshTransform.cpp 
 * @date    : 2026-01-15
 * @brief   : LuoguP4717
 */

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;

class FastWalshTransform {
private:
    using ll = long long;

    const int MOD;
    int n;
    std::vector&amp;lt;ll&amp;gt; a, b;

    auto pow2(int x) {
        ll ans = 1, a = 2;
        while (x) {
            if (x &amp;amp; 1) ans = ans * a % MOD;
            a = a * a % MOD;
            x &amp;gt;&amp;gt;= 1;
        }
        return ans;
    }
public:
    explicit FastWalshTransform(int n, int MOD) : MOD{MOD}, n{n}, a(n), b(n) {}
    FastWalshTransform(int MOD, const std::vector&amp;lt;ll&amp;gt; a, const std::vector&amp;lt;ll&amp;gt; b)
        : MOD{MOD}, n(a.size()), a{a}, b{b} {}

    void input() {
        for (int i{0}; i &amp;lt; n; i++) {
            std::cin &amp;gt;&amp;gt; a[i];
        }
        for (int i{0}; i &amp;lt; n; i++) {
            std::cin &amp;gt;&amp;gt; b[i];
        }
    }

    auto fwtXor(const std::vector&amp;lt;ll&amp;gt;&amp;amp; a, int flag) -&amp;gt; std::vector&amp;lt;ll&amp;gt; {
        auto trA{a};
        for (int o{2}, k{1}; o &amp;lt;= n; o &amp;lt;&amp;lt;= 1, k &amp;lt;&amp;lt;= 1) {
            for (int i{0}; i &amp;lt; n; i += o) {
                for (int j{0}; j &amp;lt; k; j++) {
                    auto u{trA[i+j]}, v{trA[i+j+k]};
                    trA[i+j] = (flag * (trA[i+j] + v) % MOD + MOD) % MOD;
                    trA[i+j+k] = (flag * (u - trA[i+j+k]) % MOD + MOD) % MOD;
                }
            }
        }
        return trA;
    }

    auto transform() -&amp;gt; std::vector&amp;lt;ll&amp;gt; {
        auto trA{fwtXor(a, 1)};
        auto trB{fwtXor(b, 1)};
        std::vector&amp;lt;ll&amp;gt; trC(n);
        for (int i{0}; i &amp;lt; n; i++) {
            trC[i] = trA[i] * trB[i] % MOD;
        }
        return fwtXor(trC, pow2(MOD-2));
    }
};

auto main() -&amp;gt; int {
    int n, MOD;
    std::cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; MOD;
    FastWalshTransform fwt(1&amp;lt;&amp;lt;n, MOD);
    fwt.input();
    auto ans{fwt.transform()};
    for (auto&amp;amp; p : ans) {
        std::cout &amp;lt;&amp;lt; p &amp;lt;&amp;lt; &apos; &apos;;
    }
    std::cout &amp;lt;&amp;lt; &quot;\n&quot;;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Summarize&lt;/h1&gt;
&lt;p&gt;That&apos;s all, actually these knowledge doesn&apos;t usefull in OI, but we still need learn it...&lt;/p&gt;
</content:encoded></item><item><title>Solution Report of Probability and Expectation Topic</title><link>https://blog.517group.cn/posts/202601040833/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202601040833/</guid><description>Solution of some simple problem</description><pubDate>Sun, 04 Jan 2026 08:33:34 GMT</pubDate><content:encoded>&lt;h1&gt;A - Luogu P2719 搞笑世界杯&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P2719&quot;&gt;Problem Statement&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a straightforward problem. We can solve it easily using DP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DP State:&lt;/strong&gt;&lt;br /&gt;
Let $dp[i][j]$ denote the probability that A and B end up with the same type of ticket when there are $i$ A-type tickets and $j$ B-type tickets remaining.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt;&lt;br /&gt;
Obviously, the final answer is $dp[n][n]$.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Initialization:&lt;/strong&gt;&lt;br /&gt;
When only one type of ticket remains, A and B will get the same type for sure, except for the case where only one ticket is left.&lt;br /&gt;
So we have: $dp[i][0] = dp[0][i] = 1$ for $i \ge 2$.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;State Transition:&lt;/strong&gt;&lt;br /&gt;
Each ticket is sold based on a fair coin flip, so each choice has a probability of 50%. Therefore,
$$
dp[i][j] = \frac{dp[i-1][j] + dp[i][j-1]}{2}
$$&lt;/p&gt;
&lt;p&gt;I won&apos;t include the code here since the implementation is straightforward.&lt;/p&gt;
&lt;h1&gt;B - Luogu P8804 [蓝桥杯 2022 国 B] 故障&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P8804&quot;&gt;Problem Statement&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The basic application of Bayes&apos; formula.&lt;/p&gt;
&lt;p&gt;The original statement is long and packed with information, which makes it hard to follow at first glance.&lt;br /&gt;
So the first step is to rewrite it in a more formal and structured way.&lt;/p&gt;
&lt;p&gt;Let $P(A)$ denote the probability that event $A$ occurs, and $P(A \mid B)$ denote the probability that $A$ occurs given that $B$ has occurred.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$S_i$ denotes the $i$-th fault &lt;strong&gt;Symptom&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;$C_i$ denotes the $i$-th fault &lt;strong&gt;Cause&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since both terms come with the prefix “fault” and can be confusing, we will simply use the English words &lt;strong&gt;Symptom&lt;/strong&gt; and &lt;strong&gt;Cause&lt;/strong&gt; in the following discussion.&lt;/p&gt;
&lt;p&gt;The problem provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The prior probability of each &lt;strong&gt;Cause&lt;/strong&gt; $i$, namely $P(C_i)$&lt;/li&gt;
&lt;li&gt;The conditional probability $P(S_j \mid C_i)$, meaning that &lt;strong&gt;Symptom&lt;/strong&gt; $j$ occurs given &lt;strong&gt;Cause&lt;/strong&gt; $i$&lt;/li&gt;
&lt;li&gt;A set $S$ of &lt;strong&gt;Symptoms&lt;/strong&gt; that have already been observed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our task is to compute the probability of each &lt;strong&gt;Cause&lt;/strong&gt; occurring and then sort them accordingly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important assumptions:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The system can have &lt;strong&gt;only one&lt;/strong&gt; active &lt;strong&gt;Cause&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Given a &lt;strong&gt;Cause&lt;/strong&gt;, all &lt;strong&gt;Symptoms&lt;/strong&gt; occur independently&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Based on the conditions above, this is a standard application of &lt;strong&gt;Bayes&apos; theorem&lt;/strong&gt;.&lt;br /&gt;
The probability that &lt;strong&gt;Cause&lt;/strong&gt; $i$ is responsible for the observed symptoms can be written as:&lt;/p&gt;
&lt;p&gt;$$
P(C_i \mid S) = \frac{P(S \mid C_i) \cdot P(C_i)}{P(S)}
$$&lt;/p&gt;
&lt;p&gt;For clarity, we summarize the notation again:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$C_i$: the $i$-th &lt;strong&gt;Cause&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;$S_j$: the $j$-th &lt;strong&gt;Symptom&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;$S$: the set of observed &lt;strong&gt;Symptoms&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thus, the problem reduces to efficiently computing $P(S \mid C_i)$ and $P(S)$.&lt;br /&gt;
The prior probability $P(C_i)$ is already given in the input.&lt;/p&gt;
&lt;p&gt;Since all &lt;strong&gt;Symptoms&lt;/strong&gt; are independent given a &lt;strong&gt;Cause&lt;/strong&gt;, we have:&lt;/p&gt;
&lt;p&gt;$$
P(S \mid C_i)
= \prod_{j \in S} P(S_j \mid C_i)
\times
\prod_{j \notin S} \left(1 - P(S_j \mid C_i)\right)
$$&lt;/p&gt;
&lt;p&gt;Note that $S$ represents a &lt;em&gt;specific combination&lt;/em&gt; of symptoms.&lt;br /&gt;
Therefore, we must consider not only that all symptoms in $S$ have occurred, but also that all symptoms &lt;strong&gt;not&lt;/strong&gt; in $S$ have &lt;em&gt;not&lt;/em&gt; occurred.&lt;/p&gt;
&lt;p&gt;Once $P(S \mid C_i)$ is known, computing $P(S)$ becomes straightforward.&lt;br /&gt;
Since the system can have only one active &lt;strong&gt;Cause&lt;/strong&gt;, we can treat this as a weighted sum:&lt;/p&gt;
&lt;p&gt;$$
P(S) = \sum_{i=1}^{n} P(S \mid C_i) \cdot P(C_i)
$$&lt;/p&gt;
&lt;p&gt;With these probabilities computed, we can obtain $P(C_i \mid S)$ for each &lt;strong&gt;Cause&lt;/strong&gt; and sort them as required.&lt;/p&gt;
&lt;h1&gt;D - Luogu P1297 [国家集训队] 单选错位&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P1297&quot;&gt;Problem Statement&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s analyze this case by case.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Case 1 $a_i = a_{i+1}$: In this case, it&apos;s clear that the answer for question $i+1$ is also random. The expected value is: $\frac{1}{a_i} = \frac{1}{a_{i+1}}$&lt;/li&gt;
&lt;li&gt;Case 2 $a_i &amp;gt; a_{i+1}$: only $\frac{a_{i+1}}{a_i}$ of the possible answers for question $i$ fall within the range $1 \sim a_{i+1}$. Thus, the expected value is: $\frac{a_{i+1}}{a_i} \cdot \frac{1}{a_{i+1}} = \frac{1}{a_i}$&lt;/li&gt;
&lt;li&gt;Case 3 $a_i &amp;lt; a_{i+1}$: the random answer for question $i$ is only within $1 \sim a_i$, and the probability that the correct answer for question $i+1$ falls within this range is $\frac{a_i}{a_{i+1}}$. So the expected value becomes: $\frac{a_i}{a_{i+1}} \cdot \frac{1}{a_i} = \frac{1}{a_{i+1}}$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Combining all cases, the final answer is:
$$
\sum_{i=1}^{n} \frac{1}{\max(a_i, a_{i+1})}
$$&lt;/p&gt;
&lt;h1&gt;E - Luogu P1850 [NOIP 2016 提高组] 换教室&lt;/h1&gt;
&lt;p&gt;DP State: $dp[i][j][k]$ denote for a prefix of classroom $[1, i]$, switch classroom for $j$ times, and we decide (not) to switch classroom on $i$ (record by $k$, 1 for yes and 0 for no).&lt;/p&gt;
&lt;p&gt;Transition:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int C1 = c[i - 1][0];
int C2 = c[i - 1][1];
int C3 = c[i][0];
int C4 = c[i][1];

dp[i][j][0] = std::min(
    dp[i][j][0],
    std::min(
        dp[i - 1][j][0] + mp[C1][C3], // not change anymore
        dp[i - 1][j][1]               // change on i-1 but i not
            + mp[C1][C3] * (1 - k[i - 1])
            + mp[C2][C3] * k[i - 1]
    )
);

dp[i][j][1] = std::min(
    dp[i][j][1],
    std::min(
        dp[i - 1][j - 1][0]           // change on i but i-1 not
            + mp[C1][C3] * (1 - k[i])
            + mp[C1][C4] * k[i],
        dp[i - 1][j - 1][1]           // change both i-1 and i
            + mp[C2][C4] * k[i] * k[i - 1]
            + mp[C2][C3] * k[i - 1] * (1 - k[i])
            + mp[C1][C4] * (1 - k[i - 1]) * k[i]
            + mp[C1][C3] * (1 - k[i - 1]) * (1 - k[i])
    )
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Answer is obviously.&lt;/p&gt;
&lt;h1&gt;F - Luogu P3750 [六省联考 2017] 分手是祝愿&lt;/h1&gt;
&lt;p&gt;DP State: $dp[i]$ denote the expect operator number when button quantity we need press decrease from $i$ to $i-1$.&lt;/p&gt;
&lt;p&gt;Transition:
$$
dp[i] = \frac{i}{n}\times 1 + \frac{n-i}{n} \times (dp[i]+dp[i+1]+1)
$$&lt;/p&gt;
&lt;p&gt;This transition means have $\frac i n$ probability press right button, and have $\frac{n-i}{n}$ probability press wrong button. if we press wrong button, we need pay $dp[i+1]$ to make the button quantity we need press decrease to $i$ again, and continue using $dp[i]$ times operation.&lt;/p&gt;
&lt;p&gt;then we need simplify the transition equation:
$$
dp[i] = \frac{n+(n-i)\times f[i+1]}{i}
$$&lt;/p&gt;
&lt;p&gt;Now answer is obviously.&lt;/p&gt;
&lt;h1&gt;G - Luogu P2473 [SCOI2008] 奖励关&lt;/h1&gt;
&lt;p&gt;DP State: $dp[i][S]$ denote the expected score from round $i$ to round $k$ when, after the first $i−1$ rounds, the selection state of each item is $S$.&lt;/p&gt;
&lt;p&gt;Transition:&lt;br /&gt;
For all $i\le j\le n$&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;if $S$ satisfy the state requirement of item $j$, we can decide to select it or not;&lt;/li&gt;
&lt;li&gt;or, just not select it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now answer is obiously.&lt;/p&gt;
&lt;h1&gt;Summarize&lt;/h1&gt;
&lt;p&gt;Above is the all problem of this topic.&lt;/p&gt;
&lt;p&gt;Most of problem with probability or expectation will not use very further knowledge so just solve these problem like solving basic DP problem.&lt;/p&gt;
</content:encoded></item><item><title>DP Optimize</title><link>https://blog.517group.cn/posts/202512241656/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202512241656/</guid><description>Common optimization of DP</description><pubDate>Wed, 24 Dec 2025 16:58:01 GMT</pubDate><content:encoded>&lt;h1&gt;Decision Monotonicity&lt;/h1&gt;
&lt;p&gt;Decision Monotonicity is a key concept for optimizing dynamic programming transitions.&lt;/p&gt;
&lt;p&gt;The idea of &lt;em&gt;decision monotonicity&lt;/em&gt; is that the &lt;strong&gt;decision points&lt;/strong&gt; of the DP exhibit a monotonic property. Before introducing this concept, we first define what a &lt;em&gt;decision point&lt;/em&gt; is.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definition of Decision Point&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For a fixed index $i$, an index $j$ is called a &lt;strong&gt;decision point&lt;/strong&gt; of $i$ if, for all $j&apos; &amp;lt; i$, $g(j) + w(j, i) \le g(j&apos;) + w(j&apos;, i)$.&lt;/p&gt;
&lt;p&gt;In this case, $i$ is referred to as the &lt;strong&gt;decision-affected point&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that there may be multiple decision points corresponding to the same $i$.&lt;/p&gt;
&lt;p&gt;Intuitively, a &lt;strong&gt;decision point&lt;/strong&gt; is an index that attains the optimum in the DP transition for state $i$.&lt;/p&gt;
&lt;p&gt;Having defined decision points, we can now introduce &lt;strong&gt;decision monotonicity&lt;/strong&gt;. Its precise definition depends on the specific form of the DP transition equation. In the following sections, we will discuss different cases separately.&lt;/p&gt;
&lt;h1&gt;Prefix Transition&lt;/h1&gt;
&lt;p&gt;The standard form of a &lt;strong&gt;prefix transition&lt;/strong&gt; is&lt;/p&gt;
&lt;p&gt;$$
f[i] = \min_{j&amp;lt;i} { g[j] + w(j,i) }.
$$&lt;/p&gt;
&lt;p&gt;If $f = g$, this transition is called a &lt;strong&gt;self-transition&lt;/strong&gt;.&lt;br /&gt;
Otherwise, it is called a &lt;strong&gt;heterogeneous transition&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Decision Monotonicity in Prefix Transitions&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definition (Decision Monotonicity for Prefix Transitions).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A prefix transition is said to satisfy &lt;strong&gt;decision monotonicity&lt;/strong&gt; if the following conditions hold.&lt;/p&gt;
&lt;p&gt;Let $ i_1 \le i_2 $.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For every &lt;strong&gt;decision point&lt;/strong&gt; $ j_1 $ of $ i_1 $, there exists a &lt;strong&gt;decision point&lt;/strong&gt; $ j_2 $ of $ i_2 $ such that $j_1 \le j_2$.&lt;/li&gt;
&lt;li&gt;For every &lt;strong&gt;decision point&lt;/strong&gt; $ j_2 $ of $ i_2 $, there exists a &lt;strong&gt;decision point&lt;/strong&gt; $ j_1 $ of $ i_1 $ such that $j_1 \le j_2$.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Intuitively, &lt;strong&gt;decision monotonicity&lt;/strong&gt; in prefix transitions means that the indices of decision points are &lt;strong&gt;non-decreasing&lt;/strong&gt; as the state index $ i $ increases.&lt;/p&gt;
&lt;p&gt;In the following sections, we will discuss how to optimize prefix DP transitions when the transition satisfies &lt;strong&gt;decision monotonicity&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Divide-and-Conquer Strategy&lt;/h2&gt;
&lt;p&gt;This strategy is applicable only to &lt;strong&gt;prefix heterogeneous transitions&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Algorithm Overview&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compute the &lt;strong&gt;decision point&lt;/strong&gt; for the midpoint of the current range.&lt;/li&gt;
&lt;li&gt;By &lt;strong&gt;decision monotonicity&lt;/strong&gt;, the valid range of decision points for each subrange can be restricted accordingly.&lt;/li&gt;
&lt;li&gt;As a result, each decision point is evaluated at most a constant number of times, and the overall time complexity is $O(n\log n)$.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;This algorithm is remarkably elegant. When I first encountered it, I was genuinely impressed by its simplicity and efficiency.&lt;/p&gt;
&lt;h2&gt;Quadrilateral Inequality&lt;/h2&gt;
&lt;p&gt;For &lt;strong&gt;self-transitions&lt;/strong&gt;, additional conditions are required to optimize the DP.&lt;br /&gt;
The &lt;strong&gt;quadrilateral inequality&lt;/strong&gt; is one such condition.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definition (Quadrilateral Inequality).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A function $w(i, j)$ is said to satisfy the &lt;strong&gt;quadrilateral inequality&lt;/strong&gt; if, for all $a \le b \le c \le d$, the following inequality holds:
$$
w(a, c) + w(b, d) \le w(a, d) + w(b, c).
$$
&lt;strong&gt;Remark:&lt;/strong&gt; The inequality sign here is only a formal representation. Its essential meaning is that $w(a, c) + w(b, d)$ yields a better (or no worse) cost than $w(a, d) + w(b, c)$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is a well-known theorem stating that if the cost function $w$ satisfies the
&lt;strong&gt;quadrilateral inequality&lt;/strong&gt;, then the corresponding DP transition exhibits
&lt;strong&gt;decision monotonicity&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Proof.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;According to the definition of &lt;strong&gt;decision monotonicity&lt;/strong&gt;, it suffices to verify the
following two symmetric conditions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For every &lt;strong&gt;decision point&lt;/strong&gt; $j_1$ of $i_1$, there exists a &lt;strong&gt;decision point&lt;/strong&gt;
$j_2$ of $i_2$ such that $j_1 \le j_2$.&lt;/li&gt;
&lt;li&gt;For every &lt;strong&gt;decision point&lt;/strong&gt; $j_2$ of $i_2$, there exists a &lt;strong&gt;decision point&lt;/strong&gt;
$j_1$ of $i_1$ such that $j_1 \le j_2$.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since the two cases are symmetric, we only prove the first one.&lt;/p&gt;
&lt;p&gt;Let $i_1 &amp;lt; i_2$, and suppose $j_1$ is a &lt;strong&gt;decision point&lt;/strong&gt; of $i_1$.&lt;br /&gt;
By definition, for all $j&apos; &amp;lt; j_1$, we have
$$
g[j_1] + w(j_1, i_1) \le g[j&apos;] + w(j&apos;, i_1).
$$&lt;/p&gt;
&lt;p&gt;Now let $a = j&apos;$, $b = j_1$, $c = i_1$, and $d = i_2$.&lt;br /&gt;
By the &lt;strong&gt;quadrilateral inequality&lt;/strong&gt;, it follows that
$$
w(j&apos;, i_1) + w(j_1, i_2) \le w(j&apos;, i_2) + w(j_1, i_1).
$$&lt;/p&gt;
&lt;p&gt;Adding the two inequalities above yields
$$
g[j_1] + w(j_1, i_2) \le g[j&apos;] + w(j&apos;, i_2).
$$&lt;/p&gt;
&lt;p&gt;This shows that when the decision-affected point moves from $i_1$ to $i_2$,
the index $j_1$ remains no worse than any $j&apos; &amp;lt; j_1$.&lt;br /&gt;
Therefore, the decision point of $i_2$ must be greater than or equal to $j_1$.&lt;/p&gt;
&lt;p&gt;Hence, the DP transition satisfies &lt;strong&gt;decision monotonicity&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Quadrilateral Inequality&lt;/em&gt; can be derived to possess stronger properties.&lt;br /&gt;
This requires starting from another understanding form of &lt;em&gt;Quadrilateral Inequality&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Starting from the original inequality, we can make a simple transformation:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
w(a, c) + w(b, d) &amp;amp;\le w(a, d) + w(b, c), \
w(a, d) - w(a, c) &amp;amp;\ge w(b, d) - w(b, c), \
\bigl(g[a] + w(a, d)\bigr) - \bigl(g[a] + w(a, c)\bigr)
&amp;amp;\ge
\bigl(g[b] + w(b, d)\bigr) - \bigl(g[b] + w(b, c)\bigr).
\end{aligned}
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How should we interpret this inequality?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When the decision-affected point moves from $c$ to $d$,&lt;br /&gt;
the increase in cost when choosing index $a$ as the &lt;strong&gt;decision point&lt;/strong&gt;
is no smaller than the increase when choosing index $b$ as the &lt;strong&gt;decision point&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We call this phenomenon &lt;strong&gt;Gradual Deterioration&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Intuitively, &lt;em&gt;Gradual Deterioration&lt;/em&gt; provides another way to understand
&lt;strong&gt;decision monotonicity&lt;/strong&gt; under the &lt;strong&gt;quadrilateral inequality&lt;/strong&gt;:
as the decision-affected point increases, earlier decision points
incur higher marginal costs than later ones.
As a result, the optimal decision point shifts monotonically forward.&lt;/p&gt;
&lt;p&gt;Moreover, &lt;em&gt;Gradual Deterioration&lt;/em&gt; reveals an even stronger property:&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;ANY two indices&lt;/strong&gt; $j_1 &amp;lt; j_2$, there exists a dividing point $x$ such that&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;for decision-affected points with index less than $x$, choosing $j_1$ is better;&lt;/li&gt;
&lt;li&gt;for decision-affected points with index greater than $x$, choosing $j_2$ is better.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Obviously, a transition with this property must possess &lt;em&gt;decision monotonicity&lt;/em&gt;, but a transition with &lt;em&gt;decision monotonicity&lt;/em&gt; may not necessarily satisfy this property.&lt;/p&gt;
&lt;h2&gt;Binary-Queue&lt;/h2&gt;
&lt;p&gt;The conclusion of &lt;em&gt;Gradual Deterioration&lt;/em&gt; inspires a new idea for optimizing DP.&lt;/p&gt;
&lt;p&gt;Let $x_j$ denote the &lt;strong&gt;dividing point&lt;/strong&gt; associated with decision point $j$. If $x_{j-1} \ge x_j$, then decision point $j+1$ becomes better than $j$ before $j$ ever becomes better than $j-1$.&lt;br /&gt;
As a result, $j$ will never be an optimal &lt;strong&gt;decision point&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This observation allows us to design an algorithm that safely ignores all such decision points. By discarding them during the transition process, the DP can be computed much more efficiently.&lt;/p&gt;
&lt;p&gt;From the above derivation, we are motivated to maintain an increasing sequence of boundary points and their corresponding optimal decision points $j_1, j_2, \ldots, j_m$.&lt;/p&gt;
&lt;p&gt;Suppose we have already processed state $i-1$ and maintained the decision points $j_1, j_2, \ldots, j_m$ for $i-1$. Now we consider state $i$.
When inserting a new possible decision point $i-1$ at the back, we first compute the boundary point $x_m$ between $i-1$ and $j_m$.
If $x_{m-1} \ge x_m$, then $j_m$ will never become an optimal decision point and can
be removed.
By repeatedly applying this process, and finally inserting $i-1$, we can maintain a sequence of decision points with strictly increasing boundary points.&lt;/p&gt;
&lt;p&gt;For the current &lt;em&gt;decision-affected point&lt;/em&gt; $i$, if $j_1$ is worse than $j_2$, then all subsequent points will also prefer $j_2$, and thus $j_1$ can be safely removed. Repeating this process until $j_1$ becomes better than $j_2$, we conclude that $j_1$ is the decision point for $i$, according to the previous derivation.&lt;/p&gt;
&lt;p&gt;The above procedure can be conveniently implemented using a deque. Each decision point is inserted into and removed from the deque at most once, so this part contributes linear time complexity.&lt;/p&gt;
&lt;p&gt;The key remaining issue is how to compute the boundary point $x$. By the property of &lt;em&gt;Gradual Deterioration&lt;/em&gt;, $x$ is monotonic and thus can be found by binary search.&lt;/p&gt;
&lt;p&gt;Using binary search to compute $x$, the entire process of &lt;em&gt;self-transition&lt;/em&gt; /&lt;em&gt;heterogeneous-transition optimization&lt;/em&gt; can be accelerated to $O(n \log n)$.&lt;/p&gt;
&lt;p&gt;This method is known as the &lt;strong&gt;binary queue optimization algorithm&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Slope Optimization&lt;/h2&gt;
&lt;p&gt;We can impose additional conditions to further optimize DP.
In some transitions, the cost function $w(i,j)$ has the form
$$
w(i,j) = a(i) + b(j) + c(i)d(j).
$$&lt;/p&gt;
&lt;p&gt;This form has a very nice property.
Let us discuss under what conditions such a function $w(i,j)$ satisfies the
&lt;strong&gt;quadrilateral inequality&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;By the principle of &lt;em&gt;Gradual Deterioration&lt;/em&gt;, consider indices
$
j_1 \le j_2 \le i_1 \le i_2.
$
We have
$$
\begin{aligned}
w(j_1,i_2) - w(j_1,i_1) &amp;amp;\ge w(j_2,i_2) - w(j_2,i_1), \
c(i_2)d(j_1) - c(i_1)d(j_1) &amp;amp;\ge c(i_2)d(j_2) - c(i_1)d(j_2), \
\bigl(c(i_2) - c(i_1)\bigr)\bigl(d(j_2) - d(j_1)\bigr) &amp;amp;\le 0.
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;The derivation above shows that when $c$ and $d$ have opposite monotonicity,
the function $w$ satisfies the &lt;strong&gt;quadrilateral inequality&lt;/strong&gt;.
Since $w$ only depends on the product $c(i)d(j)$, we can always reorder the indices
so that $c$ is non-decreasing and $d$ is non-increasing.&lt;/p&gt;
&lt;p&gt;Under this condition, for a decision-affected point $i$, we only need to compute
$
b(j) + c(i)d(j).
$
This expression is exactly the value of a line evaluated at $x = c(i)$.
Therefore, the dividing point between two decision points can be obtained
by computing the intersection of two lines, rather than by binary search.&lt;/p&gt;
&lt;p&gt;This technique is known as &lt;strong&gt;Slope Optimization&lt;/strong&gt;.
It can be viewed as a special case of &lt;strong&gt;Binary Queue Optimization&lt;/strong&gt; and runs
in linear time, $O(n)$.&lt;/p&gt;
&lt;p&gt;If $c$ and $d$ do not have such monotonic properties, we can instead use a
&lt;strong&gt;Li Chao Segment Tree&lt;/strong&gt; to maintain the lines and query their values at $c(i)$,
resulting in a time complexity of $O(n \log n)$.
I have already written a detailed introduction to this method in a previous blog post.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://old.517group.cn/posts/51746/&quot;&gt;Li Chao Segment Tree&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Anti quadrilateral inequality and Binary-Stack&lt;/h2&gt;
&lt;p&gt;same with before. we called below formula &lt;em&gt;Anti Quadrilateral inequality&lt;/em&gt;.
$$
w(a,c)+w(b,d) \ge w(a,d)+w(b,c)
$$
also we have property like &lt;em&gt;Gradual Deterioration&lt;/em&gt;, we called &lt;em&gt;Gradual Optimization&lt;/em&gt;. But when a transition fit &lt;em&gt;Anti Quadrilateral inequality&lt;/em&gt; has no &lt;em&gt;Decision Monotonicity&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Why? Because a &lt;em&gt;Decision Point&lt;/em&gt; can be good when it just add in our decision, but when the index of &lt;em&gt;Decision-affected&lt;/em&gt; increasing, old &lt;em&gt;Decision Point&lt;/em&gt; will be better, so this type of transition don&apos;t has &lt;em&gt;Decision Monotonicity&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But we still can use some structure to maintain this.&lt;/p&gt;
&lt;p&gt;For a fixed state $i$, we consider all possible decision points $1, 2, \ldots, i-1$.Under  &lt;em&gt;Gradual Optimization&lt;/em&gt;, for each candidate decision point $j$,
there exists a dividing point $x_j$ such that after $x_j$, choosing $j$ becomes better
than choosing $j+1$.&lt;/p&gt;
&lt;p&gt;Now observe that if $x_{j-1} \le x_j$, then before $j$ ever becomes better than $j+1$ (at $x_j$), $j-1$ has already become better than $j$ (at $x_{j-1}$).
This means that $j$ can never be the optimal decision point at any time,
and therefore can be safely discarded.&lt;/p&gt;
&lt;p&gt;By repeatedly applying this elimination process, we obtain a sequence of
candidate decision points $j_1, j_2, \ldots, j_m.$ Moreover, if $j_k$ becomes better than $j_{k+1}$ at time $x_k$,
then the dividing points must satisfy $x_1 &amp;gt; x_2 &amp;gt; \cdots &amp;gt; x_{m-1}$.&lt;/p&gt;
&lt;p&gt;Next, consider the current state $i$.
If the last candidate decision point $j_m$ is worse than $j_{m-1}$,
then by &lt;em&gt;Gradual Optimization&lt;/em&gt;, $j_m$ will never become optimal in the future,
and thus can be removed as well.&lt;/p&gt;
&lt;p&gt;It is easy to see that once $j_m$ becomes better than $j_{m-1}$,
$j_m$ is the optimal choice for the current state $i$—that is,
the decision point for $i$.&lt;/p&gt;
&lt;h1&gt;Range Transition&lt;/h1&gt;
&lt;p&gt;This type of transition is relatively simple.
The standard form of a &lt;strong&gt;range transition&lt;/strong&gt; is&lt;/p&gt;
&lt;p&gt;$$
f[i][j] = \min_{i \le k &amp;lt; j} \bigl{ f[i][k] + f[k+1][j] + w(i,j) \bigr}.
$$&lt;/p&gt;
&lt;p&gt;If the cost function $w(i,j)$ satisfies the &lt;strong&gt;quadrilateral inequality&lt;/strong&gt;
and the following condition (which we call &lt;strong&gt;including monotonicity&lt;/strong&gt;),
$$
w(b,c) \le w(a,d),
$$
then this transition is said to have &lt;strong&gt;decision monotonicity&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Just as in prefix transitions, this property allows us to optimize the
DP to a time complexity of $O(n^2)$.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;This article discusses several techniques for &lt;strong&gt;DP optimization&lt;/strong&gt;.
Many problems involve these ideas, and dynamic programming itself
is a fundamental topic in OI.&lt;/p&gt;
&lt;p&gt;However, techniques are only tools.
What truly matters is understanding when and how to adapt them to
different problems.&lt;/p&gt;
</content:encoded></item><item><title>Hello Everyone!</title><link>https://blog.517group.cn/posts/202512241047/</link><guid isPermaLink="true">https://blog.517group.cn/posts/202512241047/</guid><description>Complete the migration of the blog</description><pubDate>Wed, 24 Dec 2025 10:47:01 GMT</pubDate><content:encoded>&lt;p&gt;As you may have noticed, my blog has undergone a major update. The old blog, previously hosted at https://old.517group.cn, is no longer being maintained.&lt;/p&gt;
&lt;p&gt;Articles from the old blog will not be migrated to this new site. All future content will be published exclusively here.&lt;/p&gt;
&lt;p&gt;Thank you all for your continued support.&lt;/p&gt;
</content:encoded></item></channel></rss>