<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ゆき</title><description>ヴぃら</description><link>https://fuyuki.fun/</link><language>zh_CN</language><item><title>XCPC板子</title><link>https://fuyuki.fun/posts/xcpc/xcpc%E6%9D%BF%E5%AD%90/</link><guid isPermaLink="true">https://fuyuki.fun/posts/xcpc/xcpc%E6%9D%BF%E5%AD%90/</guid><pubDate>Tue, 16 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;线性结构&lt;/h2&gt;
&lt;h3&gt;KMP&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class KMP2 {
private:
    std::vector&amp;lt;std::vector&amp;lt;int&amp;gt;&amp;gt; dp;
    std::string pat;

public:
    KMP2(std::string pat) : pat(pat) {
        // dp[状态][字符] = 下个状态
        dp = std::vector&amp;lt;std::vector&amp;lt;int&amp;gt;&amp;gt;(pat.length(), std::vector&amp;lt;int&amp;gt;(256, 0));
        // base case
        dp[0][pat[0]] = 1;
        // 影子状态 X 初始为 0
        int X = 0;
        // 构建状态转移图
        for (size_t j = 1; j &amp;lt; pat.length(); j++) {
            for (size_t c = 0; c &amp;lt; 256; c++)
                dp[j][c] = dp[X][c];
            dp[j][pat[j]] = j + 1;
            // 更新影子状态
            X = dp[X][pat[j]];
        }
    }

    int search(std::string txt, size_t pos = 0) {
        // pat 的初始态为 0
        int j = 0;
        for (size_t i = pos; i &amp;lt; txt.length(); i++) {
            // 计算 pat 的下一个状态
            j = dp[j][txt[i]];
            // 到达终止态，返回结果
            if (j == pat.length()) return i - pat.length() + 1;
        }
        // 没到达终止态，匹配失败
        return -1;
    };
};

class KMP {
    std::string pattern;
    vector&amp;lt;int&amp;gt; next;

    void buildNext() {
        for (int i = 1, j = 0; i &amp;lt; pattern.size(); i++) {
            while (j &amp;amp;&amp;amp; pattern[i] != pattern[j]) j = next[j - 1];
            if (pattern[i] == pattern[j]) j++;
            next[i] = j;
        }
    }

public:
    auto searchAll(std::string txt) {
        std::vector&amp;lt;size_t&amp;gt; p;
        for (size_t i = 0, j = 0; i &amp;lt; txt.size(); i++) {
            while (j &amp;amp;&amp;amp; txt[i] != pattern[j]) j = next[j - 1];
            if (txt[i] == pattern[j]) j++;
            if (j == pattern.size()) {
                p.emplace_back(i - pattern.size() + 1);
                j = next[j - 1];
            }
        }
        return p;
    }

    size_t search(std::string txt, size_t pos = 0) {
        for (size_t i = pos, j = 0; i &amp;lt; txt.size(); i++) {
            while (j &amp;amp;&amp;amp; txt[i] != pattern[j]) j = next[j - 1];
            if (txt[i] == pattern[j]) j++;
            if (j == pattern.size()) {
                return i - pattern.size() + 1;
            }
        }
        return -1;
    }

    KMP(std::string pattern) : pattern(pattern) {
        next.resize(pattern.size() + 1);
        buildNext();
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Manacher 求回文串&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct Manacher {

    // 求奇数回文串
    std::vector&amp;lt;int&amp;gt; static getOddPalindrome(const std::string &amp;amp;s) {
        std::vector&amp;lt;int&amp;gt; d1(s.size());
        for (int i = 0, l = 0, r = -1; i &amp;lt; s.size(); i++) {
            int k = (i &amp;gt; r) ? 1 : std::min(d1[l + r - i], r - i + 1);
            while (0 &amp;lt;= i - k &amp;amp;&amp;amp; i + k &amp;lt; s.size() &amp;amp;&amp;amp; s[i - k] == s[i + k]) {
                k++;
            }
            d1[i] = k--;
            if (i + k &amp;gt; r) {
                l = i - k;
                r = i + k;
            }
        }
        return d1;
    }

    // 求偶数回文串
    std::vector&amp;lt;int&amp;gt; static getEvenPalindrome(const std::string &amp;amp;s) {
        std::vector&amp;lt;int&amp;gt; d2(s.size());
        for (int i = 0, l = 0, r = -1; i &amp;lt; s.size(); i++) {
            int k = (i &amp;gt; r) ? 0 : std::min(d2[l + r - i + 1], r - i + 1);
            while (0 &amp;lt;= i - k - 1 &amp;amp;&amp;amp; i + k &amp;lt; s.size() &amp;amp;&amp;amp;
                    s[i - k - 1] == s[i + k]) {
                k++;
            }
            d2[i] = k--;
            if (i + k &amp;gt; r) {
                l = i - k - 1;
                r = i + k;
            }
        }
        return d2;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;字符串哈希(1-based)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;size_t HashBase = 233&amp;gt;
class StringHash {
    std::vector&amp;lt;size_t&amp;gt; hash;
    std::vector&amp;lt;size_t&amp;gt; hashPow;

public:
    std::string str;

    explicit StringHash() = default;

    explicit StringHash(const std::string &amp;amp;str) :
            str(str),
            hash(std::vector&amp;lt;size_t&amp;gt;(str.size())),
            hashPow(std::vector&amp;lt;size_t&amp;gt;(str.size())) {
        hashPow[0] = 1;
        hash[0] = 0;
        for (size_t i = 1; i &amp;lt; str.size(); i++) {
            hash[i] = (hash[i - 1] * HashBase + str[i]);
            hashPow[i] = hashPow[i - 1] * HashBase;
        }
    }

    constexpr size_t getHash(size_t l, size_t r) const {
        assert(r &amp;gt;= l);
        assert(l &amp;gt; 0);
        assert(r &amp;lt; str.size());
        return hash[r] - hash[l - 1] * hashPow[r - l + 1];
    }

    int compare(size_t l1, size_t r1, size_t l2, size_t r2) const {
        assert(r1 &amp;gt;= l1 &amp;amp;&amp;amp; r2 &amp;gt;= l2);
        assert(l1 &amp;gt; 0 &amp;amp;&amp;amp; l2 &amp;gt; 0);
        assert(r1 &amp;lt; str.size() &amp;amp;&amp;amp; r2 &amp;lt; str.size());
        size_t l = 0, r = std::min(r1 - l1, r2 - l2) + 1;
        size_t ans;
        while (l &amp;lt;= r) {
            size_t mid = (l + r) / 2;
            if (getHash(l1, l1 + mid - 1) == getHash(l2, l2 + mid - 1)) {
                l = mid + 1;
                ans = mid;
            } else {
                r = mid - 1;
            }
        }
        if (ans == std::min(r1 - l1, r2 - l2) + 1) {
            if (r1 - l1 == r2 - l2) {
                return 0;
            } else if (r1 - l1 &amp;gt; r2 - l2) {
                return 1;
            } else {
                return -1;
            }
        } else {
            return (str[l1 + ans] &amp;gt; str[l2 + ans]) ? 1 : -1;
        }
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;最小表示法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int k = 0, i = 0, j = 1;
while (k &amp;lt; n &amp;amp;&amp;amp; i &amp;lt; n &amp;amp;&amp;amp; j &amp;lt; n) {
  if (sec[(i + k) % n] == sec[(j + k) % n]) {
    k++;
  } else {
    sec[(i + k) % n] &amp;gt; sec[(j + k) % n] ? i = i + k + 1 : j = j + k + 1;
    if (i == j) i++;
    k = 0;
  }
}
i = min(i, j);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;后缀数组&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;$sa[i]$ 表示将所有后缀排序后第 $i$ 小的后缀的编号&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$rank[i]$ 表示后缀 $i$ 的排名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$height[i]$ 第 $i$ 名的后缀与它前一名的后缀的最长公共前缀&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$lcp(s[i], s[j]) = \min{height[i+1...j]}$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class SuffixArray {
public:
    std::vector&amp;lt;size_t&amp;gt; sa;
    std::vector&amp;lt;size_t&amp;gt; rank;
    std::vector&amp;lt;size_t&amp;gt; height;
    explicit SuffixArray() = default;

    explicit SuffixArray(const std::string &amp;amp;str) :
            sa(std::vector&amp;lt;size_t&amp;gt;(str.size())),
            rank(std::vector&amp;lt;size_t&amp;gt;(str.size())),
            height(std::vector&amp;lt;size_t&amp;gt;(str.size())) {
        StringHash&amp;lt;233&amp;gt; sh = StringHash&amp;lt;233&amp;gt;(str);
        for (size_t i = 1; i &amp;lt; str.size(); i++) {
            sa[i] = i;
        }
        std::sort(sa.begin() + 1, sa.end(), [&amp;amp;](size_t a, size_t b) -&amp;gt; bool {
            return sh.compare(a, str.size() - 1, b, str.size() - 1) == -1;
        });
        for (size_t i = 1; i &amp;lt; str.size(); i++) {
            rank[sa[i]] = i;
        }
        for (size_t i = 1, k = 0; i &amp;lt; str.size(); i++) {
            if (rank[i] == 1) {
                k = 0;
            } else {
                if (k) {
                    k--;
                }
                while (str[i + k] == str[sa[rank[i] - 1] + k]) {
                    k++;
                }
            }
            height[rank[i]] = k;
        }
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ST表&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#ifndef __SPARSE_TABLE_HPP
#define __SPARSE_TABLE_HPP

// SpareTable&amp;lt;ElemType, CompareType&amp;gt; ElemType: 元素类型 CompareType: 比较函数,
// 默认为greater&amp;lt;&amp;gt;
template &amp;lt;class T = long long, class Cmp = std::greater&amp;lt;T&amp;gt; &amp;gt;
struct SparseTable {
   private:
    std::vector&amp;lt;std::vector&amp;lt;T&amp;gt; &amp;gt; st;

   public:
    explicit SparseTable() = default;
    explicit SparseTable(const std::vector&amp;lt;T&amp;gt; &amp;amp;&amp;amp;a) {
        st.resize(a.size(), std::vector&amp;lt;T&amp;gt;(32));
        for (int i = 0; i &amp;lt; a.size(); i++) {
            st[i][0] = a[i];
        }
        for (int j = 1; 1 &amp;lt;&amp;lt; j &amp;lt;= a.size(); j++) {
            for (int i = 0; i + (1 &amp;lt;&amp;lt; j) - 1 &amp;lt; a.size(); i++) {
                st[i][j] = Cmp(st[i][j - 1], st[i + (1 &amp;lt;&amp;lt; (j - 1))][j - 1])
                               ? st[i][j - 1]
                               : st[i + (1 &amp;lt;&amp;lt; (j - 1))][j - 1];
            }
        }
    }

    T get(int l, int r) {
        assert(r &amp;gt;= l);
        assert(l &amp;gt;= 0);
        assert(r &amp;lt; st.size());
        int k = std::__lg(r - l + 1);
        return Cmp(st[l][k], st[r - (1 &amp;lt;&amp;lt; k) + 1][k]) ? st[l][k]
                                                      : st[r - (1 &amp;lt;&amp;lt; k) + 1][k];
    }
};

#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;AC自动机&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;size_t base = 26&amp;gt;
class Automaton {
private:
    std::array&amp;lt;std::vector&amp;lt;int&amp;gt;, base&amp;gt; tr;
    std::vector&amp;lt;int&amp;gt; count;
    std::vector&amp;lt;int&amp;gt; fail;
    int tot = 0;
public:
    // MAXSIZE 模式串的总长
    Automaton(int maxsize) : count(maxsize), fail(maxsize) {
        for (int i = 0; i &amp;lt; base; ++i) tr[i].resize(maxsize);
    }

    void insert(const std::string &amp;amp;s, int id) {
        int u = 0;
        for (int i = 0; i &amp;lt; s.size(); i++) {
            int c = s[i] - &apos;a&apos;;
            if (!tr[c][u]) tr[c][u] = ++tot;
            u = tr[c][u];
        }
        count[u] = id;
    }

    void build() {
        std::queue&amp;lt;int&amp;gt; q;
        for (int i = 0; i &amp;lt; base; i++) if (tr[i][0]) q.push(tr[i][0]);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = 0; i &amp;lt; base; i++) {
                if (tr[i][u]) {
                    fail[tr[i][u]] = tr[i][fail[u]];
                    q.push(tr[i][u]);
                } else tr[i][u] = tr[i][fail[u]];
            }
        }
    }

    std::unordered_map&amp;lt;int, int&amp;gt; query(const std::string &amp;amp;t) {
        int u = 0;
        std::unordered_map&amp;lt;int, int&amp;gt; ans;
        for (int i = 0; i &amp;lt; t.size(); i++) {
            u = tr[t[i] - &apos;a&apos;][u];
            for (int j = u; j; j = fail[j]) {
                if (count[j]) ans[count[j]]++;
            }
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;树状结构&lt;/h2&gt;
&lt;h3&gt;Trie树&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#ifndef __TRIE_HPP
#define __TRIE_HPP

template&amp;lt;class T = char&amp;gt;
class Trie {
private:
    typedef T element_type;

    struct TrieNode {
        size_t wordCount;
        std::map&amp;lt;element_type, std::unique_ptr&amp;lt;TrieNode&amp;gt; &amp;gt; next;

        TrieNode() : wordCount(0), next(std::map&amp;lt;element_type, std::unique_ptr&amp;lt;TrieNode&amp;gt; &amp;gt;()) {}

    };

    std::unique_ptr&amp;lt;TrieNode&amp;gt; _root;

public:

    explicit Trie() { _root = std::make_unique&amp;lt;TrieNode&amp;gt;(); }

    TrieNode* root() { return _root.get(); }

    void clear() { _root = std::make_unique&amp;lt;TrieNode&amp;gt;(); }

    TrieNode* insert(const std::basic_string&amp;lt;element_type&amp;gt; &amp;amp;word, TrieNode* pos = nullptr) {
        auto location = (pos == nullptr) ? _root.get() : pos;
        for (char const &amp;amp;i: word) {
            if (location-&amp;gt;next[i] == nullptr) {
                location-&amp;gt;next[i] = std::make_unique&amp;lt;TrieNode&amp;gt;();
            }
            location = location-&amp;gt;next[i].get();
        }
        location-&amp;gt;wordCount++;
        return location;
    }

    TrieNode* insert(element_type character, TrieNode* pos = nullptr) {
        auto location = (pos == nullptr) ? _root.get() : pos;
        if (location-&amp;gt;next[character] == nullptr) {
            location-&amp;gt;next[character] = std::make_unique&amp;lt;TrieNode&amp;gt;();
        }
        location = location-&amp;gt;next[character].get();
        location-&amp;gt;wordCount++;
        return location;
    }

    TrieNode* find(const std::basic_string&amp;lt;element_type&amp;gt; &amp;amp;word, TrieNode* pos = nullptr) {
        auto location = (pos == nullptr) ? _root.get() : pos;
        for (int i = 0; i &amp;lt; word.length() &amp;amp;&amp;amp; location; i++)
            location = location-&amp;gt;next[word[i]].get();
        return location;
    }

    TrieNode* find(element_type character, TrieNode* pos = nullptr) {
        auto location = (pos == nullptr) ? _root.get() : pos;
        return location-&amp;gt;next[character].get();
    }

    size_t count(const std::basic_string&amp;lt;element_type&amp;gt; &amp;amp;word, TrieNode* pos = nullptr) {
        auto location = (pos == nullptr) ? _root.get() : pos;
        for (int i = 0; i &amp;lt; word.length() &amp;amp;&amp;amp; location; i++)
            location = location-&amp;gt;next[word[i]].get();
        return location == nullptr ? 0 : location-&amp;gt;wordCount;
    }

    size_t count(element_type character, TrieNode* pos = nullptr) {
        auto location = (pos == nullptr) ? _root.get() : pos;
        return location-&amp;gt;next[character] == nullptr ? 0 : location-&amp;gt;next[character]-&amp;gt;wordCount;
    }

    TrieNode* erase(const std::basic_string&amp;lt;element_type&amp;gt; &amp;amp;word, TrieNode* pos = nullptr, size_t count = 1) {
        auto location = (pos == nullptr) ? _root.get() : pos;
        for (int i = 0; i &amp;lt; word.length() &amp;amp;&amp;amp; location; i++)
            location = location-&amp;gt;next[word[i]].get();
        if (location == nullptr) return nullptr;
        location-&amp;gt;wordCount -= min(count, location-&amp;gt;wordCount);
        return location;
    }

    TrieNode* erase(element_type character, TrieNode* pos = nullptr, size_t count = 1) {
        auto location = (pos == nullptr) ? _root.get() : pos;
        if (location-&amp;gt;next[character] == nullptr) return nullptr;
        location-&amp;gt;next[character]-&amp;gt;wordCount -= min(count, location-&amp;gt;next[character]-&amp;gt;wordCount);
        return location;
    }

};

#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;线段树&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;class T = long long, class V = long long&amp;gt;
class ISegmentTree {
protected:
    typedef T element_type;
    typedef V value_type;

    struct node {
        value_type value;
        element_type add;
        element_type change;
        bool isChange{false};
        size_t id{0};
        size_t l{0}, r{0};

        constexpr bool inRange(int l, int r) {
            return this-&amp;gt;l &amp;gt;= l &amp;amp;&amp;amp; this-&amp;gt;r &amp;lt;= r;
        }

        constexpr bool outOfRange(int l, int r) {
            return this-&amp;gt;l &amp;gt; r || this-&amp;gt;r &amp;lt; l;
        }

        constexpr size_t size() { return r - l + 1; }
    };

    constexpr static size_t ls(size_t x) { return 2 * x; }

    constexpr static size_t rs(size_t x) { return 2 * x + 1; }

    std::vector&amp;lt;node&amp;gt; tree;

    // you should modify these functions
    virtual value_type assign_value(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr,
                                    size_t x) = 0;

    virtual value_type merge_value(value_type x, value_type y) = 0;

    virtual void add_value(size_t x, element_type add) = 0;

    virtual void change_value(size_t x, element_type change) = 0;

    void add_tag(size_t x, element_type add) {
        add_value(x, add);
        tree[x].add += add;
    }

    void change_tag(size_t x, element_type change) {
        change_value(x, change);
        tree[x].change = change;
        tree[x].isChange = true;
        tree[x].add = 0;
    }

    void push_down(size_t x) {
        if (tree[x].isChange) {
            change_tag(ls(x), tree[x].change);
            change_tag(rs(x), tree[x].change);
            tree[x].isChange = false;
            tree[x].change = 0;
        }
        if (tree[x].add) {
            add_tag(ls(x), tree[x].add);
            add_tag(rs(x), tree[x].add);
            tree[x].add = 0;
        }
    }

    void push_up(size_t x) {
        tree[x].value = merge_value(tree[ls(x)].value, tree[rs(x)].value);
    }

    void build(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr, size_t x, size_t l,
               size_t r) {
        tree[x].id = x;
        tree[x].l = l, tree[x].r = r;
        if (l == r) {
            // how to assign value
            tree[x].value = assign_value(arr, l);
            return;
        }
        size_t m = (l + r) &amp;gt;&amp;gt; 1;
        build(arr, ls(x), l, m);
        build(arr, rs(x), m + 1, r);
        push_up(x);
    }

    // 区间加
    void _add(size_t l, size_t r, T val, size_t x = 1) {
        if (tree[x].inRange(l, r)) {
            add_tag(x, val);
            return;
        }
        push_down(x);
        if (!tree[ls(x)].outOfRange(l, r)) _add(l, r, val, ls(x));
        if (!tree[rs(x)].outOfRange(l, r)) _add(l, r, val, rs(x));
        push_up(x);
    }

    // 区间修改
    void _change(size_t l, size_t r, T val, size_t x = 1) {
        if (tree[x].inRange(l, r)) {
            change_tag(x, val);
            return;
        }
        push_down(x);
        if (!tree[ls(x)].outOfRange(l, r)) _change(l, r, val, ls(x));
        if (!tree[rs(x)].outOfRange(l, r)) _change(l, r, val, rs(x));
        push_up(x);
    }

    // 区间查询
    value_type _query(size_t l, size_t r, size_t x = 1) {
        if (tree[x].inRange(l, r)) {
            return tree[x].value;
        }
        push_down(x);
        if (tree[ls(x)].outOfRange(l, r)) return _query(l, r, rs(x));
        if (tree[rs(x)].outOfRange(l, r)) return _query(l, r, ls(x));
        return merge_value(_query(l, r, ls(x)), _query(l, r, rs(x)));
    }

public:
    // 区间加
    void add(size_t l, size_t r, element_type val) {
        assert(l &amp;lt;= r);
        _add(l, r, val, 1);
    }

    // 区间修改
    void change(size_t l, size_t r, element_type val) {
        assert(l &amp;lt;= r);
        _change(l, r, val, 1);
    }

    // 区间查询
    value_type query(size_t l, size_t r) {
        assert(l &amp;lt;= r);
        return _query(l, r, 1);
    }
};

class SegmentSumTree : public ISegmentTree&amp;lt;&amp;gt; {
private:
    value_type assign_value(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr,
                            size_t x) override {
        return arr[x];
    }

    value_type merge_value(value_type x, value_type y) override {
        return x + y;
    }

    void add_value(size_t x, element_type val) override {
        tree[x].value += val * tree[x].size();
    }

    void change_value(size_t x, element_type val) override {
        tree[x].value = val * tree[x].size();
    }

public:
    explicit SegmentSumTree(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr) {
        tree.resize(arr.size() * 4 + 1);
        build(arr, 1, 1, arr.size() - 1);
    }
};

class SegmentMaxTree : public ISegmentTree&amp;lt;&amp;gt; {
private:
    value_type assign_value(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr,
                            size_t x) override {
        return arr[x];
    }

    value_type merge_value(value_type x, value_type y) override {
        return std::max(x, y);
    }

    void add_value(size_t x, element_type val) override { tree[x].value += val; }

    void change_value(size_t x, element_type val) override {
        tree[x].value = val;
    }

public:
    explicit SegmentMaxTree(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr) {
        tree.resize(arr.size() * 4 + 1);
        build(arr, 1, 1, arr.size() - 1);
    }
};

class SegmentMinTree : public ISegmentTree&amp;lt;&amp;gt; {
private:
    value_type assign_value(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr,
                            size_t x) override {
        return arr[x];
    }

    value_type merge_value(value_type x, value_type y) override {
        return std::min(x, y);
    }

    void add_value(size_t x, element_type val) override { tree[x].value += val; }

    void change_value(size_t x, element_type val) override {
        tree[x].value = val;
    }

public:
    explicit SegmentMinTree(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr) {
        tree.resize(arr.size() * 4 + 1);
        build(arr, 1, 1, arr.size() - 1);
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;可持久化线段树&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;$dir[i]$ 保存的第 $i$ 个版本，初始版本为 $dir[0]$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;函数接口&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;value_type assign_value(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr， size_t x)&lt;/code&gt;：数组元素初始化为线段树的值&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;value_type merge_value(value_type x, value_type y)&lt;/code&gt;：定义值的合并操作&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;namespace pst {

    using namespace std;
    constexpr int MAXN = 1e6 + 10;

    template&amp;lt;class T, class V&amp;gt;
    class IPersistentSegmentTree {
    protected:
        typedef T element_type;
        typedef V value_type;

        struct node {
            size_t ls, rs;
            value_type value;
        };

        size_t length;
        size_t total = 0;
        vector&amp;lt;node&amp;gt; tree;

        virtual value_type assign_value(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr,
                                        size_t x) = 0;

        virtual value_type merge_value(value_type x, value_type y) = 0;

        void push_up(int node) {
            tree[node].value =
                    merge_value(tree[tree[node].ls].value, tree[tree[node].rs].value);
        }

        void build(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr, size_t &amp;amp;node, size_t l,
                   size_t r) {
            node = ++total;
            if (l == r) {
                tree[node].value = assign_value(arr, l);
                return;
            }
            int mid = (l + r) &amp;gt;&amp;gt; 1;
            build(arr, tree[node].ls, l, mid);
            build(arr, tree[node].rs, mid + 1, r);
            push_up(node);
        }

        size_t _update(size_t &amp;amp;node, size_t last, size_t pos, element_type val,
                       size_t l, size_t r) {
            node = ++total;
            tree[node] = tree[last];
            if (l == r) {
                tree[node].value = val;
                return node;
            }
            int mid = (l + r) &amp;gt;&amp;gt; 1;
            if (pos &amp;lt;= mid)
                _update(tree[node].ls, tree[last].ls, pos, val, l, mid);
            else
                _update(tree[node].rs, tree[last].rs, pos, val, mid + 1, r);
            push_up(node);
            return node;
        }

        value_type _get(size_t node, size_t pos, size_t l, size_t r) {
            if (l == r) return tree[node].value;
            int mid = (l + r) &amp;gt;&amp;gt; 1;
            if (pos &amp;lt;= mid)
                return _get(tree[node].ls, pos, l, mid);
            else
                return _get(tree[node].rs, pos, mid + 1, r);
        }

    public:
        // 保存版本信息
        vector&amp;lt;size_t&amp;gt; dir;

        size_t update(size_t &amp;amp;node, size_t last, size_t pos, element_type val) {
            return _update(node, last, pos, val, 1, length);
        }

        value_type get(size_t node, size_t pos) {
            return _get(node, pos, 1, length);
        }
    };

    class PersistentSegmentTree
            : public IPersistentSegmentTree&amp;lt;long long, long long&amp;gt; {
    private:
        value_type assign_value(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr, size_t x) {
            return arr[x];
        }

        value_type merge_value(value_type x, value_type y) { return x + y; }

    public:
        explicit PersistentSegmentTree(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr) {
            length = arr.size() - 1;
            dir.resize(MAXN);
            tree.resize(MAXN &amp;lt;&amp;lt; 5);
            build(arr, dir[0], 1, length);
        }
    };

    class KthMinTree : public IPersistentSegmentTree&amp;lt;int, int&amp;gt; {
    private:
        value_type assign_value(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr, size_t x) {
            return arr[x];
        }

        value_type merge_value(value_type x, value_type y) { return x + y; }

        value_type _kthMin(size_t prev, size_t now, size_t l, size_t r, size_t k) {
            if (l == r) return l;
            int mid = (l + r) &amp;gt;&amp;gt; 1;
            int x = tree[tree[now].ls].value - tree[tree[prev].ls].value;
            if (k &amp;lt;= x)
                return _kthMin(tree[prev].ls, tree[now].ls, l, mid, k);
            else
                return _kthMin(tree[prev].rs, tree[now].rs, mid + 1, r, k - x);
        }

        value_type _lessOrEqual(size_t prev, size_t now, size_t l, size_t r, element_type k) {
            if (l == r) {
                return tree[now].value - tree[prev].value;
            }
            int mid = (l + r) &amp;gt;&amp;gt; 1;
            int ans = 0;
            if (k &amp;lt;= mid) {
                ans += _lessOrEqual(tree[prev].ls, tree[now].ls, l, mid, k);
            } else {
                ans += tree[tree[now].ls].value - tree[tree[prev].ls].value;
                ans += _lessOrEqual(tree[prev].rs, tree[now].rs, mid + 1, r, k);
            }
            return ans;
        }

    public:

        KthMinTree() {
            tree.resize(MAXN &amp;lt;&amp;lt; 5);
            dir.resize(MAXN);
        }

        explicit KthMinTree(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr) {
            length = arr.size() - 1;
            dir.resize(MAXN);
            tree.resize(MAXN &amp;lt;&amp;lt; 5);
            unordered_map&amp;lt;element_type, value_type&amp;gt; cnt;
            for (int i = 1; i &amp;lt;= length; i++) {
                update(dir[i], dir[i - 1], arr[i], cnt[arr[i]] + 1);
                cnt[arr[i]] += 1;
            }
        }

        void resize(const std::vector&amp;lt;element_type&amp;gt; &amp;amp;arr) {
            total = 0;
            length = arr.size() - 1;
            unordered_map&amp;lt;element_type, int&amp;gt; cnt;
            for (int i = 1; i &amp;lt;= length; i++) {
                update(dir[i], dir[i - 1], arr[i], cnt[arr[i]] + 1);
                cnt[arr[i]] += 1;
            }
        }

        value_type kthMin(size_t l, size_t r, size_t k) {
            return _kthMin(dir[l - 1], dir[r], 1, length, k);
        }

        value_type lessOrEqual(size_t l, size_t r, element_type k) {
            return _lessOrEqual(dir[l - 1], dir[r], 1, length, k);
        }
    };
}  // namespace pst
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;可撤销并查集&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct RollingDsu {
    std::vector&amp;lt;int&amp;gt; f, siz, tag, st;

    RollingDsu() = default;

    explicit RollingDsu(int n) : f(n + 1), siz(n + 1, 1), tag(n + 1, 0) { std::iota(f.begin(), f.end(), 0); }

    int find(int x) {
        while (x != f[x]) x = f[x];
        return x;
    }

    constexpr bool same(int x, int y) { return find(x) == find(y); }

    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) return false;
        if (siz[x] &amp;lt; siz[y]) {
            std::swap(x, y);
        }
        tag[y] -= tag[x];
        siz[x] += siz[y];
        f[y] = x;
        st.emplace_back(y);
        return true;
    }

    constexpr int size() {
        return st.size();
    }

    int size(int x) {
        return siz[find(x)];
    }

    void rollBack(int lastSize = 0) {
        assert(lastSize &amp;lt;= st.size());
        while (st.size() != lastSize) {
            int y = st.back();
            int x = f[y];
            siz[x] -= siz[y];
            tag[y] += tag[x];
            f[y] = y;
            st.pop_back();
        }
    }

    void add(int x, int v) { tag[find(x)] += v; }

};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;时间线段树分治&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct TimeSegTree {

    RollingDsu dsu;

    struct node {
        int l{0}, r{0};
        std::vector&amp;lt;std::pair&amp;lt;int, int&amp;gt; &amp;gt; op;
        bool exist{false};

        constexpr bool inRange(int l, int r) {
            return this-&amp;gt;l &amp;gt;= l &amp;amp;&amp;amp; this-&amp;gt;r &amp;lt;= r;
        }

        constexpr bool outOfRange(int l, int r) {
            return this-&amp;gt;l &amp;gt; r || this-&amp;gt;r &amp;lt; l;
        }

        constexpr int length() { return this-&amp;gt;r - this-&amp;gt;l + 1; }

        constexpr int size() { return length(); }

        void add(int u, int v) { op.emplace_back(u, v); }

        void clear() { op.clear(); }
    };

    std::vector&amp;lt;node&amp;gt; tree;

    TimeSegTree() = default;

    // time:时间长度, size:图上节点个数
    explicit TimeSegTree(int time, int size) {
        dsu = RollingDsu(size);
        tree.resize(time * 4 + 5);
        build(time, 1, 1, time);
    }

    void build(int time, int u, int l, int r) {
        tree[u].l = l;
        tree[u].r = r;
        if (l == r) return;
        int mid = (l + r) &amp;gt;&amp;gt; 1;
        build(time, u &amp;lt;&amp;lt; 1, l, mid);
        build(time, u &amp;lt;&amp;lt; 1 | 1, mid + 1, r);
    }

    // 在[l, r]时间段上,连接x, y
    void modify(int l, int r, int x, int y, int u = 1) {
        if (x == y) return;
        assert(l &amp;lt;= r);
        if (tree[u].inRange(l, r)) {
            tree[u].add(x, y);
            return;
        }
        if (tree[u].outOfRange(l, r)) return;
        tree[u].exist = true;
        modify(l, r, x, y, u &amp;lt;&amp;lt; 1);
        modify(l, r, x, y, u &amp;lt;&amp;lt; 1 | 1);
    }

    void solve(int u = 1) {
        int lastSize = dsu.size();
        for (auto &amp;amp;[x, y]: tree[u].op) {
            dsu.merge(x, y);
        }
        if (!tree[u].exist) {
            // to do sth at this time
            execute(u);
            dsu.rollBack(lastSize);
            return;
        }
        solve(u &amp;lt;&amp;lt; 1);
        solve(u &amp;lt;&amp;lt; 1 | 1);
        dsu.rollBack(lastSize);
    }

    void execute(int u) {
        // how to do
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;最近公共祖先(LCA)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class LCA {
private:
    std::vector&amp;lt;std::vector&amp;lt;int&amp;gt; &amp;gt; f;
    std::vector&amp;lt;int&amp;gt; depth;

    void dfs(int root, int father, const std::vector&amp;lt;std::vector&amp;lt;int&amp;gt; &amp;gt; &amp;amp;g) {
        f[root][0] = father;
        depth[root] = depth[father] + 1;
        for (int i = 1; (1 &amp;lt;&amp;lt; i) &amp;lt;= depth[root]; i++) {
            f[root][i] = f[f[root][i - 1]][i - 1];
        }
        for (auto const &amp;amp;v: g[root]) {
            if (v == father) {
                continue;
            }
            dfs(v, root, g);
        }
    }

public:

    // g: 无向边构成的树, root: 根节点, 默认为1
    explicit LCA(int n, const std::vector&amp;lt;std::vector&amp;lt;int&amp;gt;&amp;gt; &amp;amp;g, int root = 1) {
        f.resize(n + 1, std::vector&amp;lt;int&amp;gt;(32));
        depth.resize(n + 1);
        dfs(root, 0, g);
    }

    // 返回u和v的最近公共祖先
    int query(int u, int v) {
        if (depth[u] &amp;lt; depth[v]) {
            std::swap(u, v);
        }
        while (depth[u] &amp;gt; depth[v]) {
            u = f[u][std::__lg(depth[u] - depth[v])];
        }
        if (u == v) {
            return u;
        }
        for (int i = std::__lg(depth[u]); i &amp;gt;= 0; i--) {
            if (f[u][i] != f[v][i]) {
                u = f[u][i];
                v = f[v][i];
            }
        }
        return f[u][0];
    }

    // 返回u和v之间的距离
    int distance(int u, int v) {
        return depth[u] + depth[v] - 2 * depth[query(u, v)];
    }

};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;图结构&lt;/h2&gt;
&lt;h3&gt;2-SAT&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct TwoSat {
    int n;
    std::vector&amp;lt;std::vector&amp;lt;int&amp;gt;&amp;gt; e;
    std::vector&amp;lt;bool&amp;gt; ans;
    TwoSat(int n) : n(n), e(2 * n), ans(n) {}
    void addClause(int u, bool f, int v, bool g) {
        e[2 * u + !f].push_back(2 * v + g);
        e[2 * v + !g].push_back(2 * u + f);
    }
    bool satisfiable() {
        std::vector&amp;lt;int&amp;gt; id(2 * n, -1), dfn(2 * n, -1), low(2 * n, -1);
        std::vector&amp;lt;int&amp;gt; stk;
        int now = 0, cnt = 0;
        std::function&amp;lt;void(int)&amp;gt; tarjan = [&amp;amp;](int u) {
            stk.push_back(u);
            dfn[u] = low[u] = now++;
            for (auto v : e[u]) {
                if (dfn[v] == -1) {
                    tarjan(v);
                    low[u] = std::min(low[u], low[v]);
                } else if (id[v] == -1) {
                    low[u] = std::min(low[u], dfn[v]);
                }
            }
            if (dfn[u] == low[u]) {
                int v;
                do {
                    v = stk.back();
                    stk.pop_back();
                    id[v] = cnt;
                } while (v != u);
                ++cnt;
            }
        };
        for (int i = 0; i &amp;lt; 2 * n; ++i) if (dfn[i] == -1) tarjan(i);
        for (int i = 0; i &amp;lt; n; ++i) {
            if (id[2 * i] == id[2 * i + 1]) return false;
            ans[i] = id[2 * i] &amp;gt; id[2 * i + 1];
        }
        return true;
    }
    std::vector&amp;lt;bool&amp;gt; answer() { return ans; }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tarjan求强连通分量&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const int maxn = 1e5 + 5;
stack&amp;lt;int&amp;gt; stk;
// instk:是否在栈中  scc:每个点所属的强连通分量编号  cscc:强连通分量的数量 sz:强连通分量的大小
int dfsn[maxn], low[maxn], instk[maxn], scc[maxn], dfsncnt = 0, cscc = 0, sz[maxn];
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; e(maxn);
void tarjan(int p) {
    low[p] = dfsn[p] = ++dfsncnt;
    instk[p] = 1;
    stk.push(p);
    for (auto q: e[p]) {
        if (!dfsn[q]) {
            tarjan(q);
            low[p] = min(low[p], low[q]);
        } else if (instk[q]) {
            low[p] = min(low[p], dfsn[q]);
        }
    }
    if (low[p] == dfsn[p]) {
        int top;
        cscc++;
        do {
            top = stk.top();
            stk.pop();
            instk[top] = 0;
            scc[top] = cscc;
            sz[cscc]++;
        } while (top != p);
    }
}

for (int i = 1; i &amp;lt;= n; i++) {
    if (!dfsn[i]) {
        tarjan(i);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tarjan求割点&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const int N = 1e5 + 5;
vector&amp;lt;int&amp;gt; edge[N];
int dfn[N], root[N];
int ct;
vector&amp;lt;int&amp;gt; cut_points;

void tarjan(int u, bool isroot)
{
    int tot = 0;
    root[u] = dfn[u] = ++ct;
    for (auto v : edge[u])
    {
        if (!dfn[v])
        {
            tarjan(v, 0);
            root[u] = min(root[u], root[v]);
            tot += root[v] &amp;gt;= dfn[u];
        }
        else
            root[u] = min(root[u], dfn[v]);
    }
    if ((isroot &amp;amp;&amp;amp; tot &amp;gt;= 2) || (!isroot &amp;amp;&amp;amp; tot &amp;gt;= 1))
        cut_points.emplace_back(u);
}

tarjan(1, 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tarjan求割边&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const int N = 1e5 + 5;
vector&amp;lt;int&amp;gt; edge[N];
int dfn[N], root[N], fa[N], ct;
struct node
{
    int u, v;
};
vector&amp;lt;node&amp;gt; cut_edge;
void tarjan(int u)
{
    int tot = 0;
    root[u] = dfn[u] = ++ct;
    for (auto v : edge[u])
    {
        if (!dfn[v])
        {
            fa[v] = u;
            tarjan(v);
            root[u] = min(root[u], root[v]);
            if (root[v] &amp;gt; dfn[u])
                cut_edge.emplace_back(node{u, v});
        }
        else if (v != fa[u])
            root[u] = min(root[u], dfn[v]);
    }
}
tarjan(1);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;费用流&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
using i64 = long long;
struct MCFGraph {
    struct Edge {
        int v, c, f;
        Edge(int v, int c, int f) : v(v), c(c), f(f) {}
    };
    const int n;
    vector&amp;lt;Edge&amp;gt; e;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; g;
    vector&amp;lt;i64&amp;gt; h, dis;
    vector&amp;lt;int&amp;gt; pre;
    bool dijkstra(int s, int t) {
        dis.assign(n, numeric_limits&amp;lt;i64&amp;gt;::max());
        pre.assign(n, -1);
        priority_queue&amp;lt;pair&amp;lt;i64, int&amp;gt;, vector&amp;lt;pair&amp;lt;i64, int&amp;gt;&amp;gt;, greater&amp;lt;pair&amp;lt;i64, int&amp;gt;&amp;gt;&amp;gt; que;
        dis[s] = 0;
        que.emplace(0, s);
        while (!que.empty()) {
            i64 d = que.top().first;
            int u = que.top().second;
            que.pop();
            if (dis[u] &amp;lt; d) continue;
            for (int i : g[u]) {
                int v = e[i].v;
                int c = e[i].c;
                int f = e[i].f;
                if (c &amp;gt; 0 &amp;amp;&amp;amp; dis[v] &amp;gt; d + h[u] - h[v] + f) {
                    dis[v] = d + h[u] - h[v] + f;
                    pre[v] = i;
                    que.emplace(dis[v], v);
                }
            }
        }
        return dis[t] != numeric_limits&amp;lt;i64&amp;gt;::max();
    }
    MCFGraph(int n) : n(n), g(n) {}
    void addEdge(int u, int v, int c, int f) {//c是流量,f是费用.
        if (f &amp;lt; 0) {
            g[u].push_back(e.size());
            e.emplace_back(v, 0, f);
            g[v].push_back(e.size());
            e.emplace_back(u, c, -f);
        } else {
            g[u].push_back(e.size());
            e.emplace_back(v, c, f);
            g[v].push_back(e.size());
            e.emplace_back(u, 0, -f);
        }
    }
    pair&amp;lt;int, i64&amp;gt; flow(int s, int t) {
        int flow = 0;
        i64 cost = 0;
        h.assign(n, 0);
        while (dijkstra(s, t)) {
            for (int i = 0; i &amp;lt; n; ++i) h[i] += dis[i];//更新势能
            int aug = numeric_limits&amp;lt;int&amp;gt;::max();//流量
            for (int i = t; i != s; i = e[pre[i] ^ 1].v) aug = min(aug, e[pre[i]].c);
            for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
                e[pre[i]].c -= aug;
                e[pre[i] ^ 1].c += aug;
            }
            flow += aug;
            cost += i64(aug) * h[t];
        }
        return make_pair(flow, cost);
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;杂项&lt;/h2&gt;
&lt;h3&gt;MODINT&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#if __cplusplus &amp;lt; 201703L
#define constexpr inline
#endif

template&amp;lt;long long T = 998244353&amp;gt;
struct ModInt {
    long long x;

    constexpr ModInt(const long long x = 0) : x(x % T) {
    }

    [[nodiscard]] constexpr long long val() const { return x; }

    constexpr ModInt &amp;amp;operator=(const ModInt &amp;amp;a) {
        x = a.x;
        return *this;
    }

    constexpr ModInt &amp;amp;operator=(const long long y) {
        x = y % T;
        return *this;
    }

    constexpr ModInt operator+(const ModInt &amp;amp;a) const {
        int x0 = x + a.x;
        return ModInt(x0 &amp;lt; T ? x0 : x0 - T);
    }

    constexpr ModInt operator-(const ModInt &amp;amp;a) const {
        int x0 = x - a.x;
        return ModInt(x0 &amp;lt; 0 ? x0 + T : x0);
    }

    constexpr ModInt operator*(const ModInt &amp;amp;a) const {
        return ModInt(1LL * x * a.x % T);
    }

    constexpr ModInt operator/(const ModInt &amp;amp;a) const {
        return *this * a.inv();
    }

    constexpr bool operator==(const ModInt &amp;amp;a) const { return x == a.x; };

    constexpr bool operator!=(const ModInt &amp;amp;a) const { return x != a.x; };

    constexpr void operator+=(const ModInt &amp;amp;a) {
        x += a.x;
        if (x &amp;gt;= T) x -= T;
    }

    constexpr void operator-=(const ModInt &amp;amp;a) {
        x -= a.x;
        if (x &amp;lt; 0) x += T;
    }

    constexpr void operator*=(const ModInt &amp;amp;a) { x = 1LL * x * a.x % T; }

    constexpr void operator/=(const ModInt &amp;amp;a) { *this = *this / a; }

    constexpr friend ModInt operator+(int y, const ModInt &amp;amp;a) {
        int x0 = y + a.x;
        return ModInt(x0 &amp;lt; T ? x0 : x0 - T);
    }

    constexpr friend ModInt operator-(int y, const ModInt &amp;amp;a) {
        int x0 = y - a.x;
        return ModInt(x0 &amp;lt; 0 ? x0 + T : x0);
    }

    constexpr friend ModInt operator*(int y, const ModInt &amp;amp;a) {
        return ModInt(1LL * y * a.x % T);
    }

    constexpr friend ModInt operator/(int y, const ModInt &amp;amp;a) {
        return ModInt(y) / a;
    }

    constexpr friend std::ostream &amp;amp;operator&amp;lt;&amp;lt;(std::ostream &amp;amp;os, const ModInt &amp;amp;a) {
        return os &amp;lt;&amp;lt; a.x;
    }

    constexpr friend std::istream &amp;amp;operator&amp;gt;&amp;gt;(std::istream &amp;amp;is, ModInt &amp;amp;t) {
        return is &amp;gt;&amp;gt; t.x;
    }

    constexpr ModInt pow(int n) const {
        ModInt res(1), mul(x);
        while (n) {
            if (n &amp;amp; 1) res *= mul;
            mul *= mul;
            n &amp;gt;&amp;gt;= 1;
        }
        return res;
    }

    constexpr ModInt operator^(const int n) const { return pow(n); }

    constexpr ModInt inv() const {
        int a = x, b = T, u = 1, v = 0;
        while (b) {
            int t = a / b;
            a -= t * b;
            std::swap(a, b);
            u -= t * v;
            std::swap(u, v);
        }
        if (u &amp;lt; 0) u += T;
        return u;
    }

    constexpr ModInt operator~() const { return inv(); }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Random&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;std::mt19937 sd(std::random_device{}());
std::uniform_int_distribution&amp;lt;unsigned&amp;gt; rd1(min,max);
std::uniform_real_distribution&amp;lt;double&amp;gt; rd2(min,max); //均匀分布
std::normal_distribution&amp;lt;double&amp;gt; rd3(min,max); //正态分布
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;pbds&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;ext/pb_ds/assoc_container.hpp&amp;gt;
#include &amp;lt;ext/pb_ds/hash_policy.hpp&amp;gt;
#include &amp;lt;ext/pb_ds/priority_queue.hpp&amp;gt;
#include &amp;lt;ext/pb_ds/tree_policy.hpp&amp;gt;
#include &amp;lt;ext/pb_ds/trie_policy.hpp&amp;gt;
#include &amp;lt;ext/rope&amp;gt;
using namespace __gnu_pbds;
using namespace __gnu_cxx;
template &amp;lt;typename T, class P = std::less&amp;lt;T&amp;gt;&amp;gt;
using ordered_set =
    tree&amp;lt;T, null_type, P, rb_tree_tag, tree_order_statistics_node_update&amp;gt;;
/*
定义一颗红黑树
int 关键字类型
null_type无映射(低版本g++为null_mapped_type)
less&amp;lt;int&amp;gt;从小到大排序
rb_tree_tag 红黑树（splay_tree_tag）
tree_order_statistics_node_update结点更新
插入t.insert();
删除t.erase();
求k在树中是第几大:t.order_of_key();
求树中的第k大:t.find_by_order();
前驱:t.lower_bound();
后继t.upper_bound();
a.join(b)b并入a 前提是两棵树的key的取值范围不相交
a.split(v,b)key小于等于v的元素属于a，其余的属于b
T.lower_bound(x)   &amp;gt;=x的min的迭代器
T.upper_bound((x)  &amp;gt;x的min的迭代器
T.find_by_order(k) 第k个数比它小的数
*/

template &amp;lt;typename T, class P = std::less&amp;lt;T&amp;gt;&amp;gt;
using heap = __gnu_pbds::priority_queue&amp;lt;T, P, pairing_heap_tag&amp;gt;;
/*
push()  //会返回一个迭代器
top()  //同 stl
size()  //同 stl
empty() //同 stl
clear()  //同 stl
pop()  //同 stl
join(priority_queue &amp;amp;other)  //合并两个堆,other会被清空
split(Pred prd,priority_queue &amp;amp;other)  //分离出两个堆
modify(point_iterator it,const key)  //修改一个节点的值
erase(point_iterator it) //删除一个节点
*/

template &amp;lt;typename T, typename P = std::less&amp;lt;T&amp;gt;&amp;gt;
using cmap = cc_hash_table&amp;lt;T, P&amp;gt;; // 拉链法(内存小)
template &amp;lt;typename T, typename P = std::less&amp;lt;T&amp;gt;&amp;gt;
using gmap = gp_hash_table&amp;lt;T, P&amp;gt;; // 查探法(速度快)

using trie =
    __gnu_pbds::trie&amp;lt;std::string, null_type, trie_string_access_traits&amp;lt;&amp;gt;,
                     pat_trie_tag, trie_prefix_search_node_update&amp;gt;;

/*
tr.insert(string s); //插入s
tr.erase(string s); //删除s
tr.join(trie b); //将b并入tr
pair//pair的使用如下：
pair&amp;lt;tr::iterator,tr::iterator&amp;gt; range=base.prefix_range(string x);
for(tr::iterator it=range.first;it!=range.second;it++)
        cout&amp;lt;&amp;lt;*it&amp;lt;&amp;lt;&apos; &apos;&amp;lt;&amp;lt;endl;
//pair中第一个是起始迭代器，第二个是终止迭代器，遍历过去就可以找到所有字符串了。
*/

rope&amp;lt;int&amp;gt; *r[100000];

/*
push_back(type x): 在末尾插入x
insert(int pos, type *s,int n):
将字符串s的前n位插入rope的下标pos处，如没有参数n则将字符串s的所有位都插入rope的下标pos处
append(type *s,int pos,int n):
把字符串s中从下标pos开始的n个字符连接到rope的结尾，如没有参数n则把字符串s中下标pos后的所有字符连接到rope的结尾，如没有参数pos则把整个字符串s连接到rope的结尾
erase(int pos, int x): 在pos处删除x个元素
length(): 返回数组长度
size(): 返回数组长度
replace(pos, type *s):
从rope的下标pos开始替换成字符串x，x的长度为从pos开始替换的位数，如果pos后的位数不够就补足
substr(int pos, int len): 从pos处开始提取len个元素
copy(int pos, int x, type *s):
从rope的下标pos开始的len个字符用s代替，如果pos后的位数不够就补足
at(int x):
访问第x个元素，同rp[x]
拷贝历史版本: r[i] = new rope(*r[i - 1]);
*/
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>ArchWsl的一些注意事项和技巧</title><link>https://fuyuki.fun/posts/linux/archwsl-tips/</link><guid isPermaLink="true">https://fuyuki.fun/posts/linux/archwsl-tips/</guid><description>ArchWsl的一些注意事项和技巧</description><pubDate>Thu, 19 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h4&gt;font&lt;/h4&gt;
&lt;h5&gt;将windows字体共享到wsl中&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo ln -s /mnt/c/Windows/Fonts /usr/share/fonts/windows
sudo fc-cache -fv
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;切换默认字符集为zh_CN.UTF-8&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo vim /etc/locale.gen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;删掉文件中 &lt;code&gt;# zh_CN.UTF-8&lt;/code&gt; 前的 &lt;code&gt;#&lt;/code&gt;&lt;/p&gt;
&lt;h5&gt;下载语言包&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo locale-gen
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;在&lt;code&gt;.zshrc&lt;/code&gt;或&lt;code&gt;.bashrc&lt;/code&gt;前加入&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;export LANG=zh_CN.UTF-8
export LANGUAGE=zh_CN:en_US
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;重启wsl&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;wsl --shutdown
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;wslg修复&lt;/h4&gt;
&lt;h5&gt;修复X11&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo rm -r /tmp/.X11-unix
ln -s /mnt/wslg/.X11-unix /tmp/.X11-unix
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;修复wayland&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;ln -sf /mnt/wslg/runtime-dir/wayland-0* /run/user/1000/
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;中文输入法&lt;/h4&gt;
&lt;h5&gt;安装fcitx5&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo pacman -S fcitx5-im fcitx5-chinese-addons
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;在&lt;code&gt;.zshrc&lt;/code&gt;或&lt;code&gt;.bashrc&lt;/code&gt;前加入&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;export QT_IM_MODULE=fcitx
export GTK_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
export SDL_IM_MODULE=fcitx
export GLFW_IM_MODULE=ibus
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;创建dbus的用户进程&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo loginctl enable-linger $USER
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;禁用wayland&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;# 在配置文件 /home/$USER/.config/fcitx5/config 后面追加 以下内容
[Behavior/DisabledAddons]
0=wayland
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;启动fcitx5&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;fcitx5&amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;加入拼音输入&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;fcitx5-configtool
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将&lt;code&gt;简体中文（中国）&lt;/code&gt; &amp;gt; &lt;code&gt;Pinyin&lt;/code&gt; 加入左侧&lt;/p&gt;
&lt;h4&gt;清理空间&lt;/h4&gt;
&lt;h5&gt;清理安装包缓存&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo pacman -Scc
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;清理孤立的软件包&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo pacman -Rns $(pacman -Qtdq)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;清理日志&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;sudo journalctl --vacuum-size=50M
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>原型与原型链</title><link>https://fuyuki.fun/posts/javascript/%E5%8E%9F%E5%9E%8B%E4%B8%8E%E5%8E%9F%E5%9E%8B%E9%93%BE/</link><guid isPermaLink="true">https://fuyuki.fun/posts/javascript/%E5%8E%9F%E5%9E%8B%E4%B8%8E%E5%8E%9F%E5%9E%8B%E9%93%BE/</guid><description>原型与原型链的概念及其在JavaScript中的应用</description><pubDate>Tue, 14 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;什么是原型&lt;/h2&gt;
&lt;p&gt;在 JavaScript 中，每个对象都有一个内部属性 &lt;code&gt;__proto__&lt;/code&gt;，这个属性指向另一个对象，这个对象就是该对象的原型。通过原型，对象可以继承原型对象的属性和方法。类似于 lua 中的元表（&lt;code&gt;metatable&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;当试图访问一个对象的属性时，它不仅仅在该对象上搜寻，还会搜寻该对象的原型，以及该对象的原型的原型，依次层层向上搜索，直到找到一个名字匹配的属性或到达原型链的末尾。&lt;/p&gt;
&lt;p&gt;准确地说，这些属性和方法定义在Object的构造器函数（constructor functions）的 &lt;code&gt;prototype&lt;/code&gt; 属性上，而不是直接定义在对象本身上。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function doSomething() {}
console.log(doSomething.prototype.constructor === doSomething); // true
const obj = new doSomething();
console.log(obj.__proto__ === doSomething.prototype); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，原型对象有一个自有属性 &lt;code&gt;constructor&lt;/code&gt;，这个属性指向该构造函数本身。&lt;/p&gt;
&lt;h2&gt;什么是原型链&lt;/h2&gt;
&lt;p&gt;原型对象也可能拥有原型，并从中继承方法和属性，一层一层、以此类推。 这种层层相连的原型对象就形成了所谓的“原型链”（prototype chain）。&lt;/p&gt;
&lt;p&gt;在对象实例和它的构造器之间建立一个链接（它是 &lt;code&gt;__proto__&lt;/code&gt; 属性，是从构造函数的 &lt;code&gt;prototype&lt;/code&gt; 属性派生的），之后通过上溯原型链，在构造器中找到这些属性和方法。&lt;/p&gt;
&lt;p&gt;原型链的顶端是 &lt;code&gt;Object.prototype&lt;/code&gt;，它的原型是 &lt;code&gt;null&lt;/code&gt;。这意味着所有对象最终都继承自 &lt;code&gt;Object.prototype&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;函数本身也是一种对象，所以函数也有自己的也有 &lt;code&gt;__proto__&lt;/code&gt; 属性，它指向 &lt;code&gt;Function.prototype&lt;/code&gt;，而 &lt;code&gt;Function.prototype&lt;/code&gt; 的原型又是 &lt;code&gt;Object.prototype&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function doSomething() {}
console.log(doSomething.__proto__ === Function.prototype); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;doSomething
    ↓ (__proto__)
Function.prototype
    ↓ (__proto__)
Object.prototype
    ↓ (__proto__)
null
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;特别的: &lt;code&gt;Function.__proto__&lt;/code&gt; 与 &lt;code&gt;Object.__proto__&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;console.log(Object instanceof Function); // true
console.log(Function instanceof Object); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;二者都返回 &lt;code&gt;true&lt;/code&gt;
说明:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Object&lt;/code&gt; 是由 &lt;code&gt;Function&lt;/code&gt; 构造的（因为 &lt;code&gt;Object&lt;/code&gt; 是函数）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Function&lt;/code&gt; 也是由 &lt;code&gt;Object&lt;/code&gt; 构造的（因为 &lt;code&gt;Function&lt;/code&gt; 是对象）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以 &lt;code&gt;Object&lt;/code&gt; 和 &lt;code&gt;Function&lt;/code&gt; 之间形成了一个循环引用。&lt;/p&gt;
&lt;p&gt;分析:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Function.__proto__&lt;/code&gt; 指向 &lt;code&gt;Function.prototype&lt;/code&gt; 因为 &lt;code&gt;Function&lt;/code&gt; 是一个函数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Object.__proto__&lt;/code&gt; 指向 &lt;code&gt;Function.prototype&lt;/code&gt; 因为 &lt;code&gt;Object&lt;/code&gt; 是一个函数。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;console.log(Function.__proto__ === Function.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;于是整个原型链如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;console.log(Function.__proto__ === Function.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Function ──────┐
               ↓ (__proto__)
Object ────→ Function.prototype
               ↓ (__proto__)
             Object.prototype
               ↓ (__proto__)
             null
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;实现 instanceof&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;instanceof&lt;/code&gt; 运算符用于检测构造函数的 &lt;code&gt;prototype&lt;/code&gt; 属性是否存在于某个实例对象的原型链上。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function instanceOf(instance, constructor) {
  if (
    (typeof instance !== &quot;object&quot; &amp;amp;&amp;amp; typeof instance !== &quot;function&quot;) ||
    instance === null
  ) {
    return false; // 基础类型直接返回 false
  }
  let proto = Object.getPrototypeOf(instance); // 获取实例的原型
  const prototype = constructor.prototype; // 获取构造函数的原型

  while (proto !== null) {
    if (proto === prototype) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }
  return false;
}

console.log(instanceOf([], Array)); // true
console.log(instanceOf([], Object)); // true
console.log(instanceOf({}, Array)); // false
console.log(instanceOf(function () {}, Function)); // true
console.log(instanceOf(function () {}, Object)); // true
console.log(instanceOf(null, Object)); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;所有函数（包括 &lt;code&gt;Object&lt;/code&gt;）都是 &lt;code&gt;Function&lt;/code&gt; 的实例，因此它们的 &lt;code&gt;__proto__&lt;/code&gt; 都指向 &lt;code&gt;Function.prototype&lt;/code&gt;。
所有对象（包括 &lt;code&gt;Function&lt;/code&gt;）都是 &lt;code&gt;Object&lt;/code&gt; 的实例，因此它们的 &lt;code&gt;__proto__&lt;/code&gt; 都指向 &lt;code&gt;Object.prototype&lt;/code&gt;。
&lt;code&gt;Function.prototype&lt;/code&gt; 的原型是 &lt;code&gt;Object.prototype&lt;/code&gt;，而 &lt;code&gt;Object.prototype&lt;/code&gt; 的原型是 &lt;code&gt;null&lt;/code&gt;。&lt;/p&gt;
</content:encoded></item><item><title>柯里化（Currying）</title><link>https://fuyuki.fun/posts/javascript/%E6%9F%AF%E9%87%8C%E5%8C%96/</link><guid isPermaLink="true">https://fuyuki.fun/posts/javascript/%E6%9F%AF%E9%87%8C%E5%8C%96/</guid><description>柯里化的作用与实现</description><pubDate>Tue, 07 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;什么是柯里化（Currying）&lt;/h2&gt;
&lt;p&gt;柯里化（Currying）是将一个接受多个参数的函数转换为一系列只接受单一参数的函数的技术。换句话说，柯里化将一个多参数函数拆分成多个单参数函数，每个函数返回下一个函数，直到所有参数都被提供为止。
例如，假设有一个函数 &lt;code&gt;add(a, b)&lt;/code&gt;，它接受两个参数并返回它们的和。通过柯里化，我们可以将其转换为如下形式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function add(a) {
  return function (b) {
    return a + b;
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用柯里化后的函数可以这样调用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function add(a) {
  return function (b) {
    return a + b;
  };
}
const add5 = add(5); // 返回一个新函数，等待第二个参数
const result = add5(3); // 传入第二个参数，返回结果
console.log(result); // 输出 8
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;柯里化的优点&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;部分应用&lt;/strong&gt;：柯里化允许你创建部分应用的函数，即预先填充一些参数，生成一个新的函数。这在需要多次调用同一函数但参数不同的情况下非常有用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提高代码复用性&lt;/strong&gt;：通过柯里化，可以创建更通用的函数，减少重复代码，提高代码的复用性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;函数组合&lt;/strong&gt;：柯里化使得函数组合变得更加容易，因为每个函数只处理一个参数，可以更灵活地组合不同的函数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;延迟执行&lt;/strong&gt;：柯里化允许你延迟函数的执行，直到所有参数都被提供为止，这在某些情况下可以提高性能。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如，我们有一个用于格式化和输出信息的日志函数 &lt;code&gt;log(level, message)&lt;/code&gt;，通过柯里化，我们可以创建一个专门用于错误日志的函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const errorLog = curry(log)(&quot;error&quot;);
errorLog(&quot;Something went wrong&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在，&lt;code&gt;errorLog&lt;/code&gt; 函数只需要传入消息参数，而日志级别已经被预设为 &quot;error&quot;。&lt;/p&gt;
&lt;h2&gt;实现柯里化的通用函数&lt;/h2&gt;
&lt;p&gt;下面是一个实现柯里化的通用函数 &lt;code&gt;curry&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function curry(fn) {
  return function curried(...args) {
    if (args.length &amp;gt;= fn.length) {
      return fn.apply(this, args);
    } else {
      return function (...args2) {
        return curried.apply(this, args.concat(args2));
      };
    }
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;curry&lt;/code&gt; 函数接受一个函数 &lt;code&gt;fn&lt;/code&gt; 作为参数，并返回一个新的函数 &lt;code&gt;curried&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curried&lt;/code&gt; 函数使用剩余参数语法 &lt;code&gt;...args&lt;/code&gt; 来收集传入的参数。&lt;/li&gt;
&lt;li&gt;如果传入的参数数量大于或等于原函数 &lt;code&gt;fn&lt;/code&gt; 的参数数量，则调用 &lt;code&gt;fn&lt;/code&gt; 并返回结果。&lt;/li&gt;
&lt;li&gt;如果参数数量不足，则返回一个新的函数，继续收集参数，直到参数数量满足要求。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function curry(fn) {
  return function curried(...args) {
    if (args.length &amp;gt;= fn.length) {
      return fn.apply(this, args);
    } else {
      return function (...args2) {
        return curried.apply(this, args.concat(args2));
      };
    }
  };
}
function sum(a, b, c) {
  return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 输出 6
console.log(curriedSum(1, 2)(3)); // 输出 6
console.log(curriedSum(1)(2, 3)); // 输出 6
console.log(curriedSum(1, 2, 3)); // 输出 6
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;结论&lt;/h2&gt;
&lt;p&gt;柯里化是一种强大的函数式编程技术，可以提高代码的灵活性和复用性。通过将多参数函数转换为一系列单参数函数，柯里化使得函数的部分应用、组合和延迟执行变得更加容易。在实际开发中，合理使用柯里化可以帮助我们编写更简洁、可维护的代码。&lt;/p&gt;
</content:encoded></item><item><title>深拷贝与浅拷贝</title><link>https://fuyuki.fun/posts/javascript/%E6%B7%B1%E6%8B%B7%E8%B4%9D%E4%B8%8E%E6%B5%85%E6%8B%B7%E8%B4%9D/</link><guid isPermaLink="true">https://fuyuki.fun/posts/javascript/%E6%B7%B1%E6%8B%B7%E8%B4%9D%E4%B8%8E%E6%B5%85%E6%8B%B7%E8%B4%9D/</guid><description>深拷贝与浅拷贝的区别和实现</description><pubDate>Tue, 07 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;数据类型&lt;/h2&gt;
&lt;p&gt;JavaScript 中的数据类型可以分为两大类：基本数据类型和引用数据类型。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;基本数据类型&lt;/strong&gt;：包括 &lt;code&gt;Number&lt;/code&gt;、&lt;code&gt;String&lt;/code&gt;、&lt;code&gt;Boolean&lt;/code&gt;、&lt;code&gt;null&lt;/code&gt;、&lt;code&gt;undefined&lt;/code&gt; 和 &lt;code&gt;Symbol&lt;/code&gt;。这些类型的值是不可变的，直接存储在栈内存中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引用数据类型&lt;/strong&gt;：包括 &lt;code&gt;Object&lt;/code&gt;、&lt;code&gt;Array&lt;/code&gt;、&lt;code&gt;Function&lt;/code&gt; 等。这些类型的值是可变的，存储在堆内存中，变量存储的是指向堆内存中实际数据的引用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;浅拷贝&lt;/h2&gt;
&lt;p&gt;浅拷贝是指创建一个新对象，这个新对象有着原始对象属性值的一份精确拷贝。如果属性值是基本数据类型，拷贝的是值本身；如果属性值是引用数据类型，拷贝的是内存地址（引用），因此两个对象会共享同一个引用对象。&lt;/p&gt;
&lt;h3&gt;实现浅拷贝的方法&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Object.assign()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);
shallowCopy.a = 10;
shallowCopy.b.c = 3;
console.log(original); // { a: 1, b: { c: 3 } }
console.log(shallowCopy); // { a: 10, b: { c: 3 } }
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;扩展运算符（Spread Operator）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
shallowCopy.a = 10;
shallowCopy.b.c = 3;
console.log(original); // { a: 1, b: { c: 3 } }
console.log(shallowCopy); // { a: 10, b: { c: 3 } }
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Array.prototype.slice(), Array.prototype.concat()&lt;/strong&gt;（用于数组）&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const originalArray = [1, 2, { a: 3 }];
const shallowCopyArray = originalArray.slice();
const anotherShallowCopyArray = originalArray.concat();
shallowCopyArray[0] = 10;
shallowCopyArray[2].a = 4;
console.log(originalArray); // [1, 2, { a: 4 }]
console.log(shallowCopyArray); // [10, 2, { a: 4 }]
console.log(anotherShallowCopyArray); // [1, 2, { a: 4 }]
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;手动实现浅拷贝函数&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;function shallowClone(obj) {
  if (obj === null || typeof obj !== &quot;object&quot;) {
    return obj; // 处理基本数据类型
  }
  const clone = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = obj[key];
    }
  }
  return clone;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;深拷贝&lt;/h2&gt;
&lt;p&gt;深拷贝是指创建一个新对象，这个新对象与原始对象完全独立，且包含原始对象所有属性的递归拷贝。如果属性值是基本数据类型，拷贝的是值本身；如果属性值是引用数据类型，拷贝的是该引用对象的一个全新副本。&lt;/p&gt;
&lt;h3&gt;实现深拷贝的方法&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JSON.parse() 和 JSON.stringify()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.b.c = 3;
console.log(original); // { a: 1, b: { c: 2 } }
console.log(deepCopy); // { a: 1, b: { c: 3 } }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种方法简单但有局限性，不能拷贝 &lt;code&gt;function&lt;/code&gt;、&lt;code&gt;undefined&lt;/code&gt;、&lt;code&gt;symbol&lt;/code&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;递归函数&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;function deepClone(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== &quot;object&quot;) {
    return obj; // 处理基本数据类型
  }
  if (obj instanceof Date) {
    return new Date(obj);
  }
  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }
  if (hash.has(obj)) {
    return hash.get(obj); // 处理循环引用
  }
  let clone = new obj.constructor();
  hash.set(obj, clone);
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], hash);
    }
  }
  return clone;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;区别&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内存分配&lt;/strong&gt;：浅拷贝只复制对象的第一层属性，引用类型的属性仍然指向同一个内存地址；深拷贝则是递归复制所有层级的属性，完全独立。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能&lt;/strong&gt;：浅拷贝通常比深拷贝更快，因为它只复制一层属性；深拷贝由于需要递归处理，性能开销较大。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：浅拷贝适用于对象属性较为简单且不包含嵌套对象的情况；深拷贝适用于需要完全独立的对象，尤其是包含嵌套对象的情况。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;循环引用&lt;/strong&gt;：浅拷贝不会处理循环引用的问题，而深拷贝需要特别处理循环引用以避免无限递归。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>蓝桥杯3</title><link>https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/%E8%93%9D%E6%A1%A5%E6%9D%AF3/</link><guid isPermaLink="true">https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/%E8%93%9D%E6%A1%A5%E6%9D%AF3/</guid><pubDate>Sat, 15 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目链接&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://vjudge.net/contest/693002&quot;&gt;蓝桥杯3&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;A. 填空问题&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;$256 * 8 / 32 * 1024 * 1024 = 67108864$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;暴力枚举&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;枚举 + 去重&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (int i = 0; i &amp;lt; 20; i++)
{
    for (int j = 0; j &amp;lt; 21; j++)
    {
        vec.push_back({i, j});
    }
}
for (int i = 0; i &amp;lt; vec.size(); i++)
{
    for (int j = i + 1; j &amp;lt; vec.size(); j++)
    {
        int x1 = vec[i].first, y1 = vec[i].second;
        int x2 = vec[j].first, y2 = vec[j].second;
        int A = x2 - x1, B = y1 - y2, C = x1 * y2 - x2 * y1;
        int gcdd = gcd(gcd(A, B), C);
        s.insert({{B / gcdd, A / gcdd}, C / gcdd});
    }
}
cout &amp;lt;&amp;lt; s.size() &amp;lt;&amp;lt; endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;找出 $n$ 的所有因子，枚举合法对&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ll n = 2021041820210418;
vector&amp;lt;ll&amp;gt; a;
for (ll i = 1; i * i &amp;lt;= n; i++) {
    if (n % i == 0) {
        a.push_back(i);
        if (i * i != n)
            a.push_back(n / i);
    }
}
int ans = 0;
for (auto x: a) {
    for (auto y: a) {
        if (n % (x * y) == 0) {
            ans++;
        }
    }
}
cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最短路模板题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// author: Qwen2.5-Max
#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

// 计算最大公约数 (GCD)
int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

// 计算最小公倍数 (LCM)
int lcm(int a, int b) {
    return (a / gcd(a, b)) * b;
}

// Dijkstra 算法求最短路径
long long dijkstra(int n) {
    // 定义优先队列 (最小堆)，存储 (距离, 结点)
    priority_queue&amp;lt;pair&amp;lt;long long, int&amp;gt;, vector&amp;lt;pair&amp;lt;long long, int&amp;gt;&amp;gt;, greater&amp;lt;&amp;gt;&amp;gt; pq;

    // 定义距离数组，初始化为无穷大
    vector&amp;lt;long long&amp;gt; dist(n + 1, LLONG_MAX);

    // 起点为结点 1，距离为 0
    dist[1] = 0;
    pq.push({0, 1});

    while (!pq.empty()) {
        auto [current_dist, u] = pq.top();
        pq.pop();

        // 如果当前距离大于记录的距离，跳过
        if (current_dist &amp;gt; dist[u]) continue;

        // 更新邻居的距离
        for (int v = max(1, u - 21); v &amp;lt;= min(n, u + 21); ++v) {
            if (v == u) continue; // 不考虑自己

            // 计算边的权重 (LCM)
            int weight = lcm(u, v);

            // 更新最短距离
            if (dist[v] &amp;gt; dist[u] + weight) {
                dist[v] = dist[u] + weight;
                pq.push({dist[v], v});
            }
        }
    }

    // 返回从结点 1 到结点 n 的最短距离
    return dist[n];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n = 2021; // 图的结点数
    cout &amp;lt;&amp;lt; dijkstra(n) &amp;lt;&amp;lt; &quot;\n&quot;;

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;B. 时间显示&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;将毫秒转化成时分秒并补全前导 $0$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// author: Qwen2.5-Max
#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    long long timestamp;
    cin &amp;gt;&amp;gt; timestamp;

    // 转换为秒，并取一天内的秒数
    long long seconds = timestamp / 1000; // 舍去毫秒
    long long seconds_in_day = seconds % (24 * 60 * 60);

    // 提取小时、分钟和秒
    int hours = seconds_in_day / 3600;
    int remaining_seconds = seconds_in_day % 3600;
    int minutes = remaining_seconds / 60;
    int secs = remaining_seconds % 60;

    // 格式化输出 HH:MM:SS
    printf(&quot;%02d:%02d:%02d\n&quot;, hours, minutes, secs);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;C. 砝码称重&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;将 $n$ 个砝码凑成尽可能多的重量&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;状态表示&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;使用一个布尔数组 &lt;code&gt;dp&lt;/code&gt; 表示当前可以称出的重量集合。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dp[w] = true&lt;/code&gt; 表示重量 $w$ 可以被称出。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动态规划更新&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;对于每个砝码 $W_i$，我们有两种选择：
&lt;ul&gt;
&lt;li&gt;将砝码放在天平的一侧（增加重量）。&lt;/li&gt;
&lt;li&gt;将砝码放在天平的另一侧（减少重量）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;更新规则为：如果当前可以称出重量 $w$，则可以扩展到 $w + W_i$ 和 $|w - W_i|$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优化空间复杂度&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;因为砝码总重不超过 $10^5$，我们可以使用一个布尔数组 &lt;code&gt;dp&lt;/code&gt;，大小为 $10^5+1$，表示所有可能的重量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最终结果&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;统计 &lt;code&gt;dp&lt;/code&gt; 数组中值为 &lt;code&gt;true&lt;/code&gt; 的元素个数，即为可以称出的不同重量的种类数。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// author: Qwen2.5-Max
#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
    int N;
    cin &amp;gt;&amp;gt; N;
    vector&amp;lt;int&amp;gt; weights(N);
    for (auto &amp;amp;w : weights) cin &amp;gt;&amp;gt; w;
    // 计算砝码总重的最大值
    int max_weight = 0;
    for (int w : weights) max_weight += w;
    // 初始化 dp 数组
    vector&amp;lt;bool&amp;gt; dp(max_weight + 1, false);
    dp[0] = true; // 初始时可以称出重量 0
    // 动态规划更新
    for (int i = 0; i &amp;lt; N; ++i) {
        int W = weights[i];
        // 创建临时数组，避免覆盖当前状态
        vector&amp;lt;bool&amp;gt; temp(dp);
        for (int w = 0; w &amp;lt;= max_weight; ++w) {
            if (dp[w]) {
                // 增加重量
                if (w + W &amp;lt;= max_weight) temp[w + W] = true;
                // 减少重量
                if (w &amp;gt;= W) temp[w - W] = true;
                else temp[W - w] = true;
            }
        }
        // 更新 dp 数组
        dp = move(temp);
    }
    // 统计可以称出的不同重量的种类数
    int count = 0;
    for (int w = 1; w &amp;lt;= max_weight; ++w) {
        if (dp[w]) ++count;
    }
    cout &amp;lt;&amp;lt; count &amp;lt;&amp;lt; &quot;\n&quot;;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;D. 123&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;计算形如 $1, 1, 2, 1, 2, 3,...1,2...k$ 无限长数列的区间和&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数列分组&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第 $k$ 组包含 $k$ 个数字，分别是 $1, 2, \dots, k$。&lt;/li&gt;
&lt;li&gt;数列中第 $n$ 个数字所属的组可以通过数学公式快速确定。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;前缀和优化&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;由于 $l_i$ 和 $r_i$ 的范围可能非常大（高达 $10^{12}$），直接模拟数列不可行。&lt;/li&gt;
&lt;li&gt;我们需要通过数学公式计算任意位置的前缀和，从而高效地求解区间和。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;公式推导&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第 $k$ 组的总和为 $\text{sum}_k = \frac{k(k+1)}{2}$。&lt;/li&gt;
&lt;li&gt;前 $m$ 组的总和为 $\text{prefix&lt;em&gt;sum}_m = \sum&lt;/em&gt;{k=1}^m \frac{k(k+1)}{2}=\frac{m(m+1)(m+2)}{6} $。&lt;/li&gt;
&lt;li&gt;通过二分查找，可以快速定位某个位置属于哪一组。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;解题思路&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定位组号&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用二分查找确定某个位置 $n$ 所属的组号 $k$。&lt;/li&gt;
&lt;li&gt;每组的起始位置为 $\text{start}_k = \frac{k(k-1)}{2} + 1$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;计算前缀和&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前 $m$ 组的总和为：
$$
\text{prefix_sum}_m = \frac{m(m+1)(m+2)}{6}
$$&lt;/li&gt;
&lt;li&gt;对于任意位置 $n$，先找到其所属的组 $k$，然后计算其在组内的偏移量，得到其值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;区间和计算&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;区间和可以通过前缀和公式快速计算：
$$
\text{range_sum}(l, r) = \text{prefix_sum}&lt;em&gt;r - \text{prefix_sum}&lt;/em&gt;{l-1}
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// author: Qwen2.5-Max
#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
// 计算前 m 组的总和
long long prefix_sum_group(long long m) {
    return m * (m + 1) * (m + 2) / 6;
}
// 找到位置 n 所属的组号 k
long long find_group(long long n) {
    long long left = 1, right = 2e6; // k 的最大值约为 sqrt(2e12)
    while (left &amp;lt; right) {
        long long mid = (left + right) / 2;
        if (mid * (mid + 1) / 2 &amp;lt; n) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left;
}
// 计算位置 n 的前缀和
long long prefix_sum_position(long long n) {
    if (n == 0) return 0;
    long long k = find_group(n); // 找到 n 所属的组号
    long long start_k = k * (k - 1) / 2 + 1; // 第 k 组的起始位置
    long long offset = n - start_k; // 在第 k 组中的偏移量
    long long sum_prev_groups = prefix_sum_group(k - 1); // 前 k-1 组的总和
    long long sum_current_group = (offset + 1) * (offset + 2) / 2; // 当前组的部分和
    return sum_prev_groups + sum_current_group;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin &amp;gt;&amp;gt; T;
    while (T--) {
        long long l, r;
        cin &amp;gt;&amp;gt; l &amp;gt;&amp;gt; r;
        // 计算区间和
        long long result = prefix_sum_position(r) - prefix_sum_position(l - 1);
        cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;E. 负载均衡&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;模拟任务调度&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;使用一个数组 &lt;code&gt;remaining&lt;/code&gt; 记录每台计算机的当前剩余运算能力。&lt;/li&gt;
&lt;li&gt;使用一个优先队列（最小堆）为每台计算机维护正在运行的任务，按照任务的结束时间排序。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任务分配逻辑&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;对于每个任务：
&lt;ol&gt;
&lt;li&gt;检查当前计算机是否有已经结束的任务，并释放它们占用的算力。&lt;/li&gt;
&lt;li&gt;判断当前计算机的剩余运算能力是否足够支持新任务。&lt;/li&gt;
&lt;li&gt;如果足够，将任务加入优先队列，并更新剩余运算能力；否则输出 $-1$。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优化点&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;使用优先队列（最小堆）确保每次可以快速找到最早结束的任务。&lt;/li&gt;
&lt;li&gt;时间复杂度主要由优先队列的操作决定，单次操作的时间复杂度为 $O(\log m)$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// author: Qwen2.5-Max
#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    // 每台计算机的初始运算能力
    vector&amp;lt;long long&amp;gt; remaining(n + 1);
    for (int i = 1; i &amp;lt;= n; ++i) cin &amp;gt;&amp;gt; remaining[i];
    // 每台计算机的任务队列 (优先队列，存储 {结束时间, 算力消耗})
    vector&amp;lt;priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;, std::greater&amp;lt;&amp;gt;&amp;gt;&amp;gt; tasks(n + 1);
    // 处理每个任务
    for (int i = 0; i &amp;lt; m; ++i) {
        int a, b, c, d;
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c &amp;gt;&amp;gt; d;
        // 当前计算机编号为 b
        auto &amp;amp;pq = tasks[b];
        // 释放已经结束的任务
        while (!pq.empty() &amp;amp;&amp;amp; pq.top().first &amp;lt;= a) {
            remaining[b] += pq.top().second; // 恢复算力
            pq.pop();
        }
        // 判断是否可以接受新任务
        if (remaining[b] &amp;gt;= d) {
            // 接受任务，更新剩余算力
            remaining[b] -= d;
            pq.push({a + c, d}); // 加入任务队列 (结束时间为 a + c)
            cout &amp;lt;&amp;lt; remaining[b] &amp;lt;&amp;lt; &quot;\n&quot;;
        } else {
            // 无法接受任务
            cout &amp;lt;&amp;lt; &quot;-1\n&quot;;
        }
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;F. 异或数列&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定长度为 $n$ 的数列，双方每回合各取一个值异或，求谁最后的值最大&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;数列的所有元素 $X_1, X_2, \dots, X_n$ 的异或和为 $S = X_1 \oplus X_2 \oplus \cdots \oplus X_n$&lt;/li&gt;
&lt;li&gt;如果 $S = 0$，则无论 Alice 和 Bob 如何分配，最终两人的数值必然相等，结果为平局&lt;/li&gt;
&lt;li&gt;否则，由于高位 $1$ 的价值显然大于低位 $1$ 的价值，故从高位向低位枚举：
&lt;ol&gt;
&lt;li&gt;假设当前位是 $1$ 的数量是 $1$，则 $Alice$ 先手选择该数字后，由于低位不可能超过高位，因此 $Alice$ 必胜&lt;/li&gt;
&lt;li&gt;假设当前位为 $1$ 的数量是偶数，一个偶数只能拆分成两个奇偶性相同的非负整数，所以 $Alice$ 和 $Bob$ 在该位的值一定相同，故不影响胜负&lt;/li&gt;
&lt;li&gt;假设当前位为 $1$ 的数量是奇数，一个奇数只能拆分成两个奇偶性不同的非负整数，那么当前这一位一定能分出胜负，因此谁拿到奇数个谁就获胜。如果所有数这一位都是 $1$ ，那么先手获胜，否则后手可以选择这一位为 $0$ 的数改变先后手顺序，因此当 $n - x$ 为偶数时，$Alice$ 获胜，否则 $Bob$ 获胜。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;int&amp;gt; a(32);
    int sum = 0;
    for (int i = 0; i &amp;lt; n; i++) {
        int x;
        cin &amp;gt;&amp;gt; x;
        sum ^= x;
        for (int j = 0; j &amp;lt; 32; j++) {
            if (x &amp;amp; (1 &amp;lt;&amp;lt; j))
                a[j]++;
        }
    }
    if (!sum) {
        cout &amp;lt;&amp;lt; &quot;0\n&quot;;
        return;
    }
    for (int i = 31; i &amp;gt;= 0; i--) {
        if (a[i] % 2 == 0) {
            continue;
        }
        if (a[i] == 1) {
            cout &amp;lt;&amp;lt; &quot;1\n&quot;;
            return;
        }
        if((n - a[i]) % 2 == 0){
            cout &amp;lt;&amp;lt; &quot;1\n&quot;;
        }
        else{
            cout &amp;lt;&amp;lt; &quot;-1\n&quot;;
        }
        return;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;G. 巧克力&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;我们需要帮助小蓝购买巧克力，使得他能够吃 $x$ 天，并且总花费最小。每种巧克力有以下属性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;单价&lt;/strong&gt;：每块巧克力的价格为 $a_i$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保质期&lt;/strong&gt;：巧克力只能在接下来的 $b_i$ 天内食用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数量&lt;/strong&gt;：每种巧克力最多可以购买 $c_i$ 块。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;为了尽可能地减小吃巧克力的花费，用一个堆来维护当前没有过期的巧克力中的最小值，从最后一天开始向前枚举，并将当天没有过期的巧克力加入堆中，每天取堆的最小值，并判断数量是否耗尽即可。&lt;/p&gt;
&lt;p&gt;如果从第一天枚举，堆中的合法巧克力种类是不断减少的，那么会出现因为优先选择保质期长的最小值而无法选择保质期短的次小值，而从后往前枚举堆中的合法巧克力种类是增多而非减少的，故不会出现这种问题&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct node {
    int a, b;
    mutable int c;

    bool operator&amp;lt;(const node &amp;amp;rhs) const {
        return a &amp;gt; rhs.a;
    }
};

void idol_produce(int testCase) {
    /*Code Here*/
    int x, n;
    cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; n;
    priority_queue&amp;lt;node&amp;gt; q;
    vector&amp;lt;node&amp;gt; a(n + 1);
    for (int i = 1; i &amp;lt;= n; i++) {
        cin &amp;gt;&amp;gt; a[i].a &amp;gt;&amp;gt; a[i].b &amp;gt;&amp;gt; a[i].c;
    }
    sort(a.begin() + 1, a.end(), [](node &amp;amp;lhs, node &amp;amp;rhs) {
        return lhs.b &amp;lt; rhs.b;
    });
    ll ans = 0;
    for (int i = x; i &amp;gt;= 1; i--) {
        while (!a.empty() &amp;amp;&amp;amp; a.back().b &amp;gt;= i) {
            q.push(a.back());
            a.pop_back();
        }
        while (q.top().c == 0) {
            q.pop();
        }
        if (q.empty()) {
            cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; &apos;\n&apos;;
            return;
        }
        ans += q.top().a;
        q.top().c--;
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>训练赛3</title><link>https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/%E8%AE%AD%E7%BB%83%E8%B5%9B3/</link><guid isPermaLink="true">https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/%E8%AE%AD%E7%BB%83%E8%B5%9B3/</guid><pubDate>Sat, 15 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目链接&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://vjudge.net/contest/693318&quot;&gt;训练赛3&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;A. Submission Bait&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;先考虑只有一种权值的情况。显然此时若 $n$ 为奇数，则先手必胜，否则先手必败。&lt;/p&gt;
&lt;p&gt;然后考虑多种权值。发现若所有权值的出现次数均为偶数，则先手必败，否则先手仅需选择最大的出现次数为奇数的权值，即可令后手进入必败状态。&lt;/p&gt;
&lt;p&gt;于是仅需检查是否有一种权值出现次数为奇数即可。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    map&amp;lt;int, int&amp;gt; cnt;
    for (int i = 1; i &amp;lt;= n; i++) {
        int x;
        cin &amp;gt;&amp;gt; x;
        cnt[x]++;
    }
    if (any_of(cnt.begin(), cnt.end(), [](const pair&amp;lt;int, int&amp;gt; &amp;amp;x) {
        return x.second % 2 == 1;
    })) {
        cout &amp;lt;&amp;lt; &quot;YES\n&quot;;
    } else {
        cout &amp;lt;&amp;lt; &quot;NO\n&quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;B. Array Craft&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;令 $[y,x]$ 均为 $1$，从 $y - 1, x + 1$ 开始，分别向左右构造成 $-1/1$ 交替的形式。&lt;/p&gt;
&lt;p&gt;前缀和满足如下形式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;$1,0,1,0,1_y,2_x,1,0,1,...$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$-1,0,-1,0_y,1_x,0,1,...$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;后缀和满足如下形式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;$...,0,1,2_y,1_x,0,1,0,1$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$...,1,0,1_y,0_x,-1,0,-1$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$x, y$ 均满足要求&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    int x, y;
    cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y;
    vector&amp;lt;int&amp;gt; ans(n + 1);
    for (int i = y; i &amp;lt;= x; i++) {
        ans[i] = 1;
    }
    for (int i = x + 1; i &amp;lt;= n; i++) {
        ans[i] = -ans[i - 1];
    }
    for (int i = y - 1; i &amp;gt;= 1; i--) {
        ans[i] = -ans[i + 1];
    }
    for (int i = 1; i &amp;lt;= n; i++) {
        cout &amp;lt;&amp;lt; ans[i] &amp;lt;&amp;lt; &quot; \n&quot;[i == n];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;C. Mad MAD Sum&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;打表发现在第二次操作后，数组一定单调递增，且除了最大的权值外每种权值至少出现 $2$ 次，每次操作后，数组右移一位。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    ll sum = 0;
    vector&amp;lt;int&amp;gt; a(n + 1);
    for (int i = 1; i &amp;lt;= n; i++) {
        cin &amp;gt;&amp;gt; a[i];
    }
    for (int j = 1; j &amp;lt;= 2; j++) {
        int tmp = 0;
        map&amp;lt;int, int&amp;gt; mp;
        for (int i = 1; i &amp;lt;= n; i++) {
            sum += a[i];
            mp[a[i]]++;
            if (mp[a[i]] &amp;gt;= 2) {
                tmp = max(tmp, a[i]);
            }
            a[i] = tmp;
        }
    }
    ll tmp = 0;
    for (int i = 1; i &amp;lt;= n; i++) {
        tmp += a[i];
    }
    for (int i = n; i &amp;gt;= 1; i--) {
        sum += tmp;
        tmp -= a[i];
    }
    cout &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;D. Linear Maze&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;当访问到普通房间时，步数 $+ 1$，如果访问到特殊房间，则步数 $+ 1$ 后翻倍，显然访问到特殊房间后，回到该房间需要重新走相同的路程&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
    vector&amp;lt;int&amp;gt; a(n + 1);
    for(int i = 1;i &amp;lt;= k; i ++){
        int x;
        cin &amp;gt;&amp;gt; x;
        a[x] = 1;
    }
    ll ans = 0;
    for(int i = 1;i &amp;lt;= n; i++){
        ans ++;
        if(a[i]) ans *= 2;
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;E. Grid Puzzle&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;显然，由于 $2 \times 2$ 只能对相邻两行进行，对于 $(i, i + 1)$ 行，至多只进行 $1$ 次 $2 \times 2$ 操作，否则使用 $2$ 次覆盖行操作一定更优。&lt;/p&gt;
&lt;p&gt;故考虑 $dp$ ，令 $dp_{i,0/1/2}$ 表示覆盖了前 $i$ 行，且第 $i$ 行用 $2 \times 2$ 覆盖了第 $i + 1$ 行的 $0 / 1-2/ 3-4$ 个格子的最小操作数&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;int&amp;gt; a(n + 1);
    for (int i = 1; i &amp;lt;= n; i++)
        cin &amp;gt;&amp;gt; a[i];
    vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt; dp(n + 1, vector&amp;lt;int&amp;gt;(3, INF));
    dp[0][0] = 0;
    for (int i = 1; i &amp;lt;= n; i++) {
        if (a[i] == 0) dp[i][0] = min({dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]});
        else dp[i][0] = min({dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]}) + 1;
        if (a[i] &amp;lt;= 2) {
            dp[i][0] = min(dp[i][0], dp[i - 1][1]);
            dp[i][1] = min({dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]}) + 1;
        } else if (a[i] &amp;lt;= 4) {
            dp[i][1] = dp[i - 1][2] + 1;
            dp[i][2] = dp[i - 1][1] + 1;
        }
    }
    cout &amp;lt;&amp;lt; min({dp[n][0], dp[n][1], dp[n][2]}) &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;F. Catch the Mole&lt;/h2&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;对于树高根号分治，目标是把老鼠逼到一条链上，最后二分找到位置&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;先对一个叶子节点进行 $\sqrt{5000} = 70$ 次无效询问，这样所有树高小于 $70$ 的点必然不存在老鼠&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于树高大于 $70$ 的，从根节点开始递归询问。对于一个节点的儿子，只有树高大于 $70$ 才被询问，小于 $70$ 的直接跳过即可&lt;/p&gt;
&lt;p&gt;询问为真或者只有一个儿子的时候就直接递归进去，为假就下一个儿子因此这样的询问最多 $70$ 次。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最后二分即可完成，注意每次查询失败的时候，左右边界都要向根偏移一个位置&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const int N = 70;

int ask(int x) {
    cout &amp;lt;&amp;lt; &quot;? &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
    int res;
    cin &amp;gt;&amp;gt; res;
    return res;
}

# define answer(x) do { cout &amp;lt;&amp;lt; &quot;! &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl; return;} while (0)

void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; e(n + 1);
    for (int i = 1; i &amp;lt;= n - 1; i++) {
        int u, v;
        cin &amp;gt;&amp;gt; u &amp;gt;&amp;gt; v;
        e[u].emplace_back(v);
        e[v].emplace_back(u);
    }
    vector&amp;lt;int&amp;gt; father(n + 1, 0), h(n + 1);
    function&amp;lt;void(int, int)&amp;gt; dfs = [&amp;amp;](int u, int fa) -&amp;gt; void {
        for (auto v: e[u]) {
            if (v == fa) {
                continue;
            }
            father[v] = u;
            dfs(v, u);
            h[u] = max(h[u], h[v] + 1);
        }
    };
    dfs(1, 0);
    int tmp = find(h.begin() + 1, h.end(), 0) - h.begin();
    for (int i = 1; i &amp;lt;= N; i++) {
        if (ask(tmp) == 1) {
            answer(tmp);
        }
    }
    function&amp;lt;int(int)&amp;gt; dfs2 = [&amp;amp;](int u) -&amp;gt; int {
        vector&amp;lt;int&amp;gt; son;
        for (auto v: e[u]) {
            if (v == father[u] || h[v] &amp;lt; N) {
                continue;
            }
            son.emplace_back(v);
        }
        if (son.empty()) {
            return u;
        }
        for (auto x: son) {
            if (x == son.back() || ask(x) == 1) {
                return dfs2(x);
            }
        }
        assert(false);
    };
    int end = dfs2(1);
    vector&amp;lt;int&amp;gt; a;
    for (int i = end; i != 0; i = father[i]) {
        a.emplace_back(i);
    }
    reverse(a.begin(), a.end());
    int l = 0, r = a.size() - 1;
    while (l &amp;lt; r) {
        int mid = (l + r + 1) &amp;gt;&amp;gt; 1;
        if (ask(a[mid]) == 1) {
            l = mid;
        } else {
            r = mid - 1;
            l = max(0, l - 1);
            r = max(0, r - 1);
        }
    }
    answer(a[l]);
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>数学专题-测试题</title><link>https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/%E6%95%B0%E5%AD%A6%E4%B8%93%E9%A2%98-%E6%B5%8B%E8%AF%95%E9%A2%98/</link><guid isPermaLink="true">https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/%E6%95%B0%E5%AD%A6%E4%B8%93%E9%A2%98-%E6%B5%8B%E8%AF%95%E9%A2%98/</guid><description>数学专题-测试题 题解报告</description><pubDate>Sat, 18 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目链接&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://vjudge.net/contest/686463&quot;&gt;数学测试题&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/622/A&quot;&gt;A. Infinite Sequence&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给你一个无限整数序列： $1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, \ldots$序列按以下方式构建：首先写出数字 $1$，然后写出从 $1$ 到 $2$ 的数字，然后是从 $1$ 到 $3$ 的数字，然后是从 $1$ 到 $4$ 的数字，以此类推，问你第 $n$ 个数字是什么。&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;按照数列的构造方式，二分或直接模拟也可。数列长度的递增速度等同于等差数列求和（$1 + 2 + 3 + \ldots + n$）&lt;/p&gt;
&lt;p&gt;由于 $n \leq 10^{14}$，所以直接模拟可以在 $O(\sqrt n)$ 复杂度内解决问题。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/340/A&quot;&gt;B. The Wall&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定 $a, b, l, r$。问在 $[l, r]$ 内有多少数是 $a$ 和 $b$ 的倍数，
也就是问 $[l, r]$ 内有多少数是 $lcm(a, b)$ 的倍数。&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;利用前缀和的思想求解即可&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/844/B&quot;&gt;C. Rectangles&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定一个 $0/1$ 矩阵，你可以选择其中的一些元素，要求满足如下条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;这些元素相等。&lt;/li&gt;
&lt;li&gt;这些元素在同一行或同一列上。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;问有多少种不同的选择方法。&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;简单组合数学。&lt;/p&gt;
&lt;p&gt;比如对于某一行选择 $1$，如果这一行上 $1$ 的数量为 $c$，那么选择的方案数就是 $2^c - 1$（每个 $1$ 可以选和不选，但需要减掉空集的情况）。选择列和选择 $0$ 同理。&lt;/p&gt;
&lt;p&gt;需要注意的是单个元素会被列和行重复选择，所以最终答案需要减掉所有的单个元素，即减去 $n \times m$。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#define int long long
int qpow(int a, int b, int res = 1) {
	for (; b; res = (b &amp;amp; 1) ? 1ll * res * a : res, a = 1ll * a * a, b &amp;gt;&amp;gt;= 1);
	return res;
}
void solve() {
	int n,m;
	cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
	vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; b(n + 2,vector&amp;lt;int&amp;gt;(m + 2));
	for(int i = 1;i &amp;lt;= n;i ++){
		for(int j = 1;j &amp;lt;= m;j ++){
			cin &amp;gt;&amp;gt; b[i][j];
		}
	}
	vector&amp;lt;int&amp;gt; r(n + 2);
	vector&amp;lt;int&amp;gt; c(m + 2);
	int ans = 0;
	for(int i = 1;i &amp;lt;= n;i ++){
		for(int j = 1;j &amp;lt;= m;j ++){
			r[i] += (b[i][j] == 1);
		}
		ans += qpow(2,r[i]) - 1;
		ans += qpow(2,m - r[i]) - 1;
	}
	for(int j = 1;j &amp;lt;= m;j ++){
		for(int i = 1;i &amp;lt;= n;i ++){
			c[j] += (b[i][j] == 1);
		}
		ans += qpow(2,c[j]) - 1;
		ans += qpow(2,n - c[j]) - 1;
	}
	cout &amp;lt;&amp;lt; ans - n * m &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/272/B&quot;&gt;D. Dima and Sequence&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;定义运算规则如下：&lt;/p&gt;
&lt;p&gt;$$
\left {
\begin{array}{lr}
f(0) = 0 \
f(2x) = f(x) \
f(2x + 1) = f(x) + 1 \
\end{array}
\right.
$$&lt;/p&gt;
&lt;p&gt;给定数组 $a$，问有多少对 $(i, j)$ 使得 $f(a_i) = f(a_j)$&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;首先可以发现直接递归求解 $f(a_i)$ 的复杂度在 $O(\log a_i)$，因此可以暴力求出所有 $f(a_i)$，并用一个 &lt;code&gt;map&lt;/code&gt; 来存储 $f(x)$ 值相同的数量。&lt;/p&gt;
&lt;p&gt;假设有 $c$ 个数的 $f(x)$ 值相同，那么对答案产生 $\frac{c \times (c - 1)}{2}$ 的贡献。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void solve() {
	int n;
	cin &amp;gt;&amp;gt; n;
	map&amp;lt;int,int&amp;gt; mp;
	function&amp;lt;int(int)&amp;gt; dfs = [&amp;amp;](int x){
		if(x == 0) return 0;
		if(x % 2 == 0) return dfs(x / 2);
		return dfs(x / 2) + 1;
	};
	for(int i = 1;i &amp;lt;= n;i ++){
		int x;
		cin &amp;gt;&amp;gt; x;
		mp[dfs(x)] ++;
	}
	ll ans = 0;
	for(auto [u,v] : mp){
		ans += 1ll * v * (v - 1) / 2;
	}
	cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/1482/B&quot;&gt;E. Restore Modulo&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给你一个长为 $n$ 的数组 $a$，判断这个数组是否能通过特定的 $n$，$m$，$c$，$s$ 四个数通过以下方式生成，如果能，最大化 $m$ 的值。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$a_1 = s \mod m$&lt;/li&gt;
&lt;li&gt;对于所有 $1 \leq i \leq n$ 的 $i$，$a_i = (a_{i-1} + c) \mod m$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;首先单独处理 $c = 0$ 的情况。为此，检查对于 $1 \leq i \leq n - 1$ 中的每个 $i$，是否存在等式 $a_i = a_{i+1}$（或者换句话说，所有数字都相同）。如果这是真的，那么模可以任意大。否则，如果 $a_i = a_{i+1}$ 对至少一个 $i$ 成立，则 $c$ 必须等于零，但我们已经存在不相等的数。所以答案是 $-1$。&lt;/p&gt;
&lt;p&gt;那么，现在 $c \neq 0$，没有两个连续的数字相等。&lt;/p&gt;
&lt;p&gt;可以发现，$x+c \mod m$ 是 $x+c$ 或 $x-(m - c)$。
因此，所有正差（连续数字对之间）必须相同，所有负差也必须相同。否则，答案为 $-1$。&lt;/p&gt;
&lt;p&gt;如果没有正差异（或者类似地，如果没有负差异），则模可以任意大。
否则，模必须等于它们的和 $c + (m - c) = m$。&lt;/p&gt;
&lt;p&gt;找到 $m$ 和 $c$ 之后，只需要检查它们是否真的生成了正确的序列即可。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int a[maxn];
void solve() {
	int n;
	cin &amp;gt;&amp;gt; n;
	for (int i = 1; i &amp;lt;= n; i ++) {
		cin &amp;gt;&amp;gt; a[i];
	}
	if(n == 1){
		cout &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; &apos;\n&apos;;
		return;
	}
	bool f1 = 0,f2 = 0;
	for(int i = 1;i &amp;lt; n;i ++){
		f1 |= (a[i] == a[i + 1]);
		f2 |= (a[i] != a[i + 1]);
	}
	set&amp;lt;int&amp;gt; d1,d2;
	if(f1 &amp;amp;&amp;amp; f2){
		cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; &apos;\n&apos;;
		return;
	}
	if(f1 &amp;amp;&amp;amp; !f2){
		cout &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; &apos;\n&apos;;
		return;
	}
	for(int i = 1;i &amp;lt; n;i ++){
		if(a[i + 1] - a[i] &amp;gt; 0){
			d1.insert(a[i + 1] - a[i]);
		}else{
			d2.insert(a[i] - a[i + 1]);
		}
	}
	if(d2.empty()){
		if(d1.size() == 1){
			cout &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; &apos;\n&apos;;
		}else{
			cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; &apos;\n&apos;;
		}
		return;
	}
	if(d1.empty()){
		if(d2.size() == 1){
			cout &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; &apos;\n&apos;;
		}else cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; &apos;\n&apos;;
		return;
	}
	if(d1.size() == 1 &amp;amp;&amp;amp; d2.size() == 1){
		int c = *d1.begin();
		int m = c + *d2.begin();
		int now = a[1];
		if(now &amp;gt;= m){
			cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; &apos;\n&apos;;
			return;
		}
		for(int i = 2;i &amp;lt;= n;i ++){
			now = (now + c) % m;
			if(now != a[i]){
				cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; &apos;\n&apos;;
				return;
			}
		}
		cout &amp;lt;&amp;lt; m &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; c &amp;lt;&amp;lt; &apos;\n&apos;;
	}else{
		cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; &apos;\n&apos;;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/1485/C&quot;&gt;F. Floor and Mod&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;求有多少对 $(a, b)$ 满足 $\lfloor{\frac{a}{b}} \rfloor = a \mod b$ $(1 \leq a \leq x, 1 \leq b \leq y)$&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;容易发现满足条件的 $a, b$ 在形式上满足 $a = k \times b + k$ $(k &amp;lt; b)$&lt;/p&gt;
&lt;p&gt;由于 $k &amp;lt; b$，所以 $k \times b + k$ 的大小在 $O(k^2)$，因此可以 $O(\sqrt{x})$ 枚举 $k$，并计算有多少 $b$ 满足 $k &amp;lt; b \leq y$ 且 $k \times b + k \leq x$。这一过程可以 $O(1)$ 计算，也可以靠二分解决。前者的复杂度为 $O(T \times \sqrt{x})$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void solve() {
	ll x,y;
	cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y;
	ll ans = 0;
	for(int k = 1;k * (k + 1) + k &amp;lt;= x;k ++){
		ans += max(0ll,min(y,(x - k) / k) - k);
	}
	cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>数学专题-练习题</title><link>https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/%E6%95%B0%E5%AD%A6%E4%B8%93%E9%A2%98-%E7%BB%83%E4%B9%A0%E9%A2%98/</link><guid isPermaLink="true">https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/%E6%95%B0%E5%AD%A6%E4%B8%93%E9%A2%98-%E7%BB%83%E4%B9%A0%E9%A2%98/</guid><description>数学专题-练习题 题解报告</description><pubDate>Sat, 18 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目链接&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://vjudge.net/contest/686233&quot;&gt;数学练习题&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P1866&quot;&gt;编号&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;$N$ 个数，每个数介于 $1$ 和 $M_i$ 之间（可以为 $1$ 或 $M_i$）。对于每一个 $M_i$ 选择一个数这个数在 $1$ 到 $M_i$ 之间，且选择的数不能重复。问一共有多少选择方案。&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;从小到大排序后，假设当前你对前i个进行了选择，这 $i$ 个选择的范围在 $1$ 到 $M_i$ 之间，对于第 $M_{i+1}$ 个数它之中一定有 $i$ 个数已经被选择过了，因为已经从小到大排过序了。所以对于每个 $M_i$,它对答案的贡献是使答案乘上 $M_i - i - 1$ 的结果。所以当 $M_i &amp;lt; i - 1$ 时是无解的。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

#define int long long
const int mod = 1e9 + 7;
void sl(){
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;int&amp;gt; a(n + 1);
    for(int i = 1 ;i &amp;lt;= n ;i++){
        cin &amp;gt;&amp;gt; a[i];
    }
    sort(a.begin() + 1,a.end());
    int sum = 1;
    for(int i = 1 ;i &amp;lt;= n ;i++){
        if(a[i] &amp;lt; i){
            sum = 0;
            break;
        }
        sum = sum * (a[i] - i + 1) % mod;
    }
    cout &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; &apos;\n&apos;;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin &amp;gt;&amp;gt; t;
    while(t--) sl();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P1258&quot;&gt;小车问题&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;$A B$ 两地的距离 $s$，初始两人在 $A$ 地，人的步行速度 $a$ 和车的速度 $b$，车一次只能载一人求两人同时到达 $B$ 的所花费的最少时间。保证车速大于人的速度。&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;假设先开车带一个人行驶了 $x$ 距离，然后折返回去接第二个人，一路开到 $B$ 点，且保证两人同时到达，由此可列出方程：&lt;/p&gt;
&lt;p&gt;$$
(s - x) / a = 2 * (2 * x / (a + b) - x / b) + (s - x) / b \
x = (b + a) * s / (b + 3 * a)
$$&lt;/p&gt;
&lt;p&gt;根据 $x$ 可求出最后时间为：$x / b + (s - x) / a$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

#define int long long

void sl(){
    double s,a,b;
    cin &amp;gt;&amp;gt; s &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
    double x = (b + a) * s / (b + 3 * a);
    double t = x / b + (s - x) / a;
    cout &amp;lt;&amp;lt; fixed &amp;lt;&amp;lt; setprecision(6) &amp;lt;&amp;lt; t &amp;lt;&amp;lt; &apos;\n&apos;;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin &amp;gt;&amp;gt; t;
    while(t--) sl();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P4942&quot;&gt;小凯的数字&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;$Q$ 次询问，每次询问给一个 $l$ 和 $r$，求出&lt;/p&gt;
&lt;p&gt;$$
\overline{l(l+1)(l+2)...(r-1)r}\mod 9
$$&lt;/p&gt;
&lt;p&gt;后的结果。&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;首先观察数据范围，发现询问次数在 $10^4$ 次，每次的询问范围在 $10^{12}$。因此可以判断该题应该是一道 $O(1)$ 的结论题。&lt;/p&gt;
&lt;p&gt;首先观察 $7 \mod 9$，$70 \mod 9$，$700 \mod 9$ 等等，发现结果都是 $7$。因此可以大胆假设对于任意整数 $x$，有 $x \mod 9 = (x \times 10^y) \mod 9$（其中 $y$ 为 $0$ 到正无穷之间的整数）。&lt;/p&gt;
&lt;p&gt;而对于 $l$ 到 $r$ 拼接起来形成的巨大数，可以看做 $l \times 10^{y_l} + (l + 1) \times 10^{y_{l + 1}} + \ldots + r \times 10^{y_r}$。根据之前的假设，将 $10$ 的上标全部置为 $0$ 在 $x\mod 9$ 意义下的结果是一样的，所以原式变为 $l + (l + 1) + \ldots + r = \frac{(r - l + 1) \times (l + r)}{2}$。&lt;/p&gt;
&lt;p&gt;因为我们最后需要对答案取模，为了防止取模后奇偶性发生改变，我们需要对 $(r - l + 1)$ 或者 $(l + r)$ 单独除 $2$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

#define int long long
const int mod = 9;

void sl(){
    int l,r;
    cin &amp;gt;&amp;gt; l &amp;gt;&amp;gt; r;
    int sum;
    if((l + r) % 2 == 0){
        sum = (((l + r) / 2 % mod) * ((r - l + 1) % mod)) % mod;
    }else{
        sum = (((l + r) % mod) * ((r - l + 1) / 2 % mod)) % mod;
    }
    cout &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; &apos;\n&apos;;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin &amp;gt;&amp;gt; t;
    while(t--) sl();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P1404&quot;&gt;平均数&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给一个长度为 $n$ 的数列，我们需要找出该数列的一个连续子串，使得该子串平均数最大化，并且子串长度 $\ge m$。&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;我们使用二分答案来解决这个问题。对于当前二分出的平均值，我们要确定它的合法性，可以通过以下步骤实现：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将所有数先减去平均值。&lt;/li&gt;
&lt;li&gt;这时我们相当于是要找到一段区间，使得它的和大于 $0$&lt;/li&gt;
&lt;li&gt;对减后的数组进行前缀和计算。&lt;/li&gt;
&lt;li&gt;假设我们当前找到了 &lt;code&gt;pre[i]&lt;/code&gt;，只需要判断 &lt;code&gt;pre[1]&lt;/code&gt; 到 &lt;code&gt;pre[i - m]&lt;/code&gt; 之间的最小值是否小于 &lt;code&gt;pre[i]&lt;/code&gt;，就可以判断出是否存在这样的区间。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

#define int long long
void sl(){
    int n,m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    vector&amp;lt;int&amp;gt; a(n + 1);
    for(int i = 1 ;i &amp;lt;= n ;i ++){
        cin &amp;gt;&amp;gt; a[i];
        a[i] *= 10000;
    }
    auto check=[&amp;amp;](int x){
        vector&amp;lt;int&amp;gt; b = a,pre(n + 1);
        int mn = 0;
        for(int i = 1 ;i &amp;lt;= n ;i++){
            b[i] -= x;
            pre[i] = pre[i - 1] + b[i];
            if(i &amp;gt;= m){
                mn = min(mn,pre[i - m]);
                if(pre[i] &amp;gt; mn) return 1;
            }
        }
        return 0;
    };
    int l = 0,r = 30000000;
    while(l &amp;lt;= r){
        int mid = (l + r) &amp;gt;&amp;gt; 1;
        if(check(mid)) l = mid + 1;
        else r = mid - 1;
    }
    cout &amp;lt;&amp;lt; l / 10 &amp;lt;&amp;lt; &apos;\n&apos;;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin &amp;gt;&amp;gt; t;
    while(t--) sl();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/75/C&quot;&gt;Modified GCD&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;有 $q$ 组询问，每次询问 $a,b$ 在 $[l,r]$ 之间的最大公约数，如果不存在输出 $-1$&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;首先考虑 $a, b$ 的最大公约数 $\gcd(a, b)$ 与 $a, b$ 其他公约数之间的关系，可以发现 $a, b$ 的所有公约数均是最大公约数的因数。因此我们先处理出最大公约数的所有因数，然后对于每次询问的 $l, r$ 进行二分查找即可。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;
#define int long long

void sl( ){
    int a,b,q;
    cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; q;
    int gd = __gcd(a,b);
    set&amp;lt;int&amp;gt; p;
    for(int i = 1 ;i * i &amp;lt;= gd ;i ++){
        if(gd % i == 0){
            p.insert(i);
            p.insert(gd / i);
        }
    }
    while(q--){
        int l,r;
        cin &amp;gt;&amp;gt; l &amp;gt;&amp;gt; r;
        if(gd &amp;lt; l){
            cout &amp;lt;&amp;lt; &quot;-1\n&quot;;
            continue;
        }
        if(gd &amp;lt;= r){
            cout &amp;lt;&amp;lt; gd &amp;lt;&amp;lt; &apos;\n&apos;;
            continue;
        }
        auto it = p.upper_bound(r);
        it--;
        if(*it &amp;lt; l) cout &amp;lt;&amp;lt; &quot;-1\n&quot;;
        else cout &amp;lt;&amp;lt; *it &amp;lt;&amp;lt; &apos;\n&apos;;
    }
}

signed main( ){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //	cin&amp;gt;&amp;gt;t;
    while(t--) sl();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.luogu.com.cn/problem/P1069&quot;&gt;细胞分裂&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给你 $m_1$ 与 $m_2$，并且给你 $n$ 个数，假设第 $i$ 个数为 $S_i$，找到最小的 $k$ 使得，$ S_i ^ {k} \mod m_1^{m_2} = 0$，如果不存在这样的 $k$，输出 $-1$&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;首先我们需要知道两个结论：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;$x \mod y = 0$，将 $x$ 和 $y$ 质因数分解后，$s1$ 为 $x$ 的质因数集，$s2$ 为 $y$ 的质因数集，那么 $s2$ 为 $s1$ 的一个子集。&lt;/li&gt;
&lt;li&gt;假设 $z$ 是 $x$ 的一个质因数，$x$ 可以分解出 $num_z$ 个 $z$，而 $x^y$ 可以分解出 $num_z \times y$ 个 $z$。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;之后我们在回来看题目，要保证 $S_i^k \mod m_1^{m_2} = 0$，可以先对 $m_1$ 进行质因数分解，并且对于每一个 $S_i$ 同样进行质因数分解。如果 $S_i$ 可以通过幂次操作使得取模结果为 0，那么 $m_1$ 的每一个质因数也需要在 $S_i$ 中出现，并且操作之后的每一个质因数的数量也要大于 $m_1$ 的每一个质因数的数量乘上 $m_2$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;
#define int long long
const int N = 3e4 + 10;

vector&amp;lt;int&amp;gt; prime;
bool is[N];

void init(){
	for(int i = 2 ;i &amp;lt; N ;i ++){
		if(!is[i]){
			prime.push_back(i);
	    	for(int j = i * i ;j &amp;lt; N ;j += i){
				is[j] = 1;
			}
		}
	}
}

void sl( ){
	int m1,m2,n;
	cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m1 &amp;gt;&amp;gt; m2;
	vector&amp;lt;int&amp;gt; a(n + 1);
	map&amp;lt;int,int&amp;gt; mp;
	int idx = 0;
	while(m1 &amp;gt; 1){
		while(m1 % prime[idx] == 0){
			mp[prime[idx]] ++;
			m1 /= prime[idx];
		}
		idx++;
	}
	for(auto it : mp){
		auto[u,v] = it;
		mp[u] *= m2;
	}
	int f = 0,num = -1;
	for(int i = 1 ;i &amp;lt;= n ;i ++){
		cin &amp;gt;&amp;gt; a[i];
	}
	for(int i = 1 ;i &amp;lt;= n ;i++){
		int flag = 1,mn = 0;
		for(auto x : mp){
			if(a[i] % x.first != 0){
				flag = 0;
				break;
			}
			int ans = 0;
			while(a[i] % x.first == 0){
				a[i] /= x.first;
				ans++;
			}
			mn = max(mn,(x.second - 1) / ans + 1);
		}
		if(flag == 0) continue;
		if(num == -1){
			num = mn;
		}else{
			num = min(num,mn);
		}
	}
	cout &amp;lt;&amp;lt; num &amp;lt;&amp;lt; &apos;\n&apos;;
}


signed main( ){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	init();
	int t = 1;
//	cin&amp;gt;&amp;gt;t;
	while(t--) sl();
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2024-2025 ICPC NERC, Kyrgyzstan Qualification Contest</title><link>https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/2024-kyrgyzstan-qualification-contest/</link><guid isPermaLink="true">https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/2024-kyrgyzstan-qualification-contest/</guid><description>2024 Kyrgyzstan Qualification Contest 题解报告</description><pubDate>Fri, 27 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目链接&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://codeforces.com/gym/105494&quot;&gt;2024-2025 ICPC NERC, Kyrgyzstan Qualification Contest&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/A&quot;&gt;A. Problem Statement&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给 $n$ 行字符串，问输入了几个回车&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;输出 $n-1$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    cout &amp;lt;&amp;lt; n - 1 &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/B&quot;&gt;B. Ant Hill&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定 $n$ 个数表示蚂蚁山中，蚂蚁的进出，蚂蚁山在任何时刻蚂蚁的数量都不为负数，求最开始至少有几只蚂蚁&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;从头开始模拟蚂蚁的进出，设 $res$ 为当前蚂蚁山的数量，$ans = -\min{res | res \leq 0}$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    ll ans = 0;
    ll p = 0;
    for(int i = 1;i &amp;lt;= n; i++){
        ll x;
        cin &amp;gt;&amp;gt; x;
        p += x;
        if(p &amp;lt;= 0) ans = max(ans, -p);
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/C&quot;&gt;C. Linear Maze&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;$n$ 个房间，每次可以从房间 $n$ 转移到房间 $n + 1$，有 $k$ 个特殊房间，如果在奇数次访问这样一个房间，&lt;strong&gt;下一步&lt;/strong&gt;则会传送到房间 $1$，问离开迷宫要经过几个房间&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;当访问到普通房间时，步数 + 1，如果访问到特殊房间，则步数 + 1后翻倍，显然访问到特殊房间后，回到该房间需要重新走相同的路程&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
    vector&amp;lt;int&amp;gt; a(n + 1);
    for(int i = 1;i &amp;lt;= k; i ++){
        int x;
        cin &amp;gt;&amp;gt; x;
        a[x] = 1;
    }
    ll ans = 0;
    for(int i = 1;i &amp;lt;= n; i++){
        ans ++;
        if(a[i]) ans *= 2;
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/D&quot;&gt;D. Grouping&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;将 $n$ 个数字分为最小数目的好数集，好数集的定义如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;集合的大小不超过 $k$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;集合的最大值和最小值之差不超过 $M$&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;排序原数组，贪心地将其放入当前集合中，若满足则放入，否则另开一个新集合&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, m, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;
    vector&amp;lt;ll&amp;gt; a(n);
    for (int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; a[i];
    sort(a.begin(), a.end());
    ll num = -INF;
    int cnt = m;
    int ans = 0;
    for (int i = 0; i &amp;lt; n; i++) {
        if (cnt == k) {
            cnt = 1;
            num = a[i];
            ans++;
        } else if (a[i] - num &amp;gt; m) {
            cnt = 1;
            num = a[i];
            ans++;
        } else {
            cnt++;
        }
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/H&quot;&gt;H. Hierarchy&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定几个公司的员工架构树，求员工数量最多的一个公司&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;DFS求每个树的大小即可&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt; son(n + 1);
    for (int i = 1; i &amp;lt;= n; i++) {
        int x;
        cin &amp;gt;&amp;gt; x;
        son[x].push_back(i);
    }
    vector&amp;lt;int&amp;gt; dp(n + 1, 0);
    int ans = 0;
    int maxn = 0;
    function&amp;lt;void(int)&amp;gt; dfs = [&amp;amp;](int u) {
        if (son[u].empty()) {
            dp[u] = 1;
            return;
        }
        for (auto v : son[u]) {
            dfs(v);
            dp[u] += dp[v];
            if (u == 0 &amp;amp;&amp;amp; dp[v] &amp;gt; maxn) {
                maxn = dp[v];
                ans = v;
            }
        }
        dp[u] += 1;
    };
    dfs(0);
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; maxn &amp;lt;&amp;lt; endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/I&quot;&gt;I. Study Day&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;参加 $n$ 次讲座，每次可以获得 $a_i$ 点知识点，可以在参加前进行冥想使得这次获得的知识点翻倍，不能连续冥想两次，问知识点最多为多少？&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;令 $dp_m(i)$ 表示第 $i$ 次未冥想获得的最多知识点，$dp_o(i)$ 表示第 $i$ 次冥想获得的最多的知识点，那么满足：&lt;/p&gt;
&lt;p&gt;$$
dp_m(i) = \max(dp_o(i - 1), dp_m(i - 1)) + a_i \
dp_o(i) = dp_m(i-1) + 2 \times a_i
$$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;vector&amp;lt;ll&amp;gt; &amp;gt; dp(2, vector&amp;lt;ll&amp;gt;(n + 1));
    vector&amp;lt;string&amp;gt; ans(2);
    for (int i = 1; i &amp;lt;= n; i++) {
        ll x;
        cin &amp;gt;&amp;gt; x;
        auto tmp = ans;
        if (dp[0][i - 1] &amp;gt; dp[1][i - 1]) {
            dp[0][i] = dp[0][i - 1] + x;
            ans[0] = tmp[0] + &apos;O&apos;;
        } else {
            dp[0][i] = dp[1][i - 1] + x;
            ans[0] = tmp[1] + &apos;O&apos;;
        }
        dp[1][i] = dp[0][i - 1] + 2 * x;
        ans[1] = tmp[0] + &apos;M&apos;;
    }
    if (dp[0][n] &amp;gt; dp[1][n]) {
        cout &amp;lt;&amp;lt; dp[0][n] &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; ans[0] &amp;lt;&amp;lt; endl;
    } else {
        cout &amp;lt;&amp;lt; dp[1][n] &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; ans[1] &amp;lt;&amp;lt; endl;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/F&quot;&gt;F. Traffic Lights&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;有 $n$ 个路口，到达一个路口花费 $a_i$，路口处红灯亮 $r_i$ 秒，绿灯亮 $g_i$ 秒，一开始红灯亮了 $d_i$ 秒，求通过所有路口花费的总时间&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;令 $time$ 为到达路口时花费的总时间，那么该路口的灯已经亮了 $time + d_i$ 秒，显然路口的灯以 $r_i + g_i$ 为周期循环，令 $rem = (time + d_i) \mod (r_i + g_i)$ ，当 $rem &amp;lt; g_i$ 时，为绿灯，直接通过，当 $rem &amp;gt; r_i$ 时为红灯，$time := time + r_i + g_i - rem$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;int&amp;gt; a(n + 1), r(n + 1), g(n + 1), d(n + 1);
    for(int i = 1; i &amp;lt;= n; i++){
        cin &amp;gt;&amp;gt; a[i];
    }
    for(int i = 1; i &amp;lt;= n; i++){
        cin &amp;gt;&amp;gt; r[i] &amp;gt;&amp;gt; g[i] &amp;gt;&amp;gt; d[i];
    }
    ll time = 0;
    for(int i = 1;i &amp;lt;= n; i++){
        time += a[i];
        ll t = time + d[i];
        ll rem = t % (r[i] + g[i]);
        if(rem &amp;lt; g[i]){
            continue;
        }
        else{
            time += r[i] + g[i] - rem;
        }
    }
    cout &amp;lt;&amp;lt; time &amp;lt;&amp;lt; endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/G&quot;&gt;G. Need More Gold&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;有 $n$ 个怪物，每个怪物打败需要 $b_i$ 个金币（不花费），打败后获得 $g_i$ 个金币，求积累 $w$ 个金币最少打败怪物的数量和顺序&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;维护优先队列，每一次选择当前可打败的价值最高的怪物。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct node {
    int id;
    ll x, y;
    bool operator&amp;lt;(const node &amp;amp;rhs) const { return x &amp;lt; rhs.x; }
};
void idol_produce(int testCase) {
    /*Code Here*/
    ll w, n;
    cin &amp;gt;&amp;gt; w &amp;gt;&amp;gt; n;
    vector&amp;lt;node&amp;gt; a(n + 1);
    for (int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; a[i].x &amp;gt;&amp;gt; a[i].y;
        a[i].id = i + 1;
    }
    sort(a.begin(), a.end(), [](node a, node b) { return a.y &amp;gt; b.y; });
    priority_queue&amp;lt;node&amp;gt; q;
    vector&amp;lt;int&amp;gt; ans;
    ll now = 0;
    while (!a.empty() &amp;amp;&amp;amp; a.back().y == 0) {
        q.push(a.back());
        a.pop_back();
    }
    while (!q.empty() &amp;amp;&amp;amp; now &amp;lt; w) {
        node t = q.top();
        q.pop();
        now += t.x;
        ans.emplace_back(t.id);
        while (!a.empty() &amp;amp;&amp;amp; a.back().y &amp;lt;= now) {
            q.push(a.back());
            a.pop_back();
        }
    }
    if (now &amp;lt; w) {
        cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; endl;
        return;
    }
    cout &amp;lt;&amp;lt; ans.size() &amp;lt;&amp;lt; endl;
    for (auto i : ans) {
        cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105494/problem/E&quot;&gt;E. Mountain Ranges&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;$n$ 座山，假如相邻两座山之间的高度差不大于 $k$，则视为同一座山脉，问最小的 $k$ 使得恰好划分为 $m$ 座山脉&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;二分 $k$，当 $ans &amp;gt; m$ 时，$k$ 向上二分，否则向上二分&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    vector&amp;lt;int&amp;gt; a(n + 1);
    for(int i = 1;i &amp;lt;= n; i++){
        cin &amp;gt;&amp;gt; a[i];
    }
    if(n == 1){
        cout &amp;lt;&amp;lt; 1 &amp;lt;&amp;lt; &apos;\n&apos;;
        return;
    }
    int l = 1, r = 1e6;
    int ans = -1;
    while(l &amp;lt;= r){
        int mid = (l + r) &amp;gt;&amp;gt; 1;
        int cnt = 1;
        for(int i = 2;i &amp;lt;= n; i++){
            if(abs(a[i] - a[i - 1]) &amp;gt; mid){
                cnt++;
            }
        }
        if(cnt &amp;gt; m){
            l = mid + 1;
        }
        else if(cnt == m){
            ans = mid;
            r = mid - 1;
        }
        else{
            r = mid - 1;
        }
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2024-12-21周赛 2024 Benelux Algorithm Programming Contest (BAPC 24)</title><link>https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/2024-12-21%E5%91%A8%E8%B5%9B/</link><guid isPermaLink="true">https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/2024-12-21%E5%91%A8%E8%B5%9B/</guid><description>2024-12-21周赛题解报告</description><pubDate>Fri, 20 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目链接&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://codeforces.com/gym/105492&quot;&gt;2024 Benelux Algorithm Programming Contest (BAPC 24)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/A&quot;&gt;A. &quot;Aaawww...&quot; or &quot;Aaayyy!!!&quot;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;模拟滚榜的流程&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;注意每次过完一道题都应该更新榜单&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct team
{
    int id;
    string problems;
};

void idol_produce(int testCase) {
    /*Code Here*/
    int n, m, p;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; p;
    vector&amp;lt;team&amp;gt; teams(n + 1);
    int sum = 0;
    for(int i = 1;i &amp;lt;= n; i++){
        cin &amp;gt;&amp;gt; teams[i].problems;
        teams[i].id = i;
        sum += count(teams[i].problems.begin(), teams[i].problems.end(), &apos;P&apos;);
    }
    int id = n;
    for(int i = 1;i &amp;lt;= sum; i++){
        string s;
        cin &amp;gt;&amp;gt; s &amp;gt;&amp;gt; s;
        while(teams[id].problems.find(&apos;P&apos;) == string::npos) id--;
        if (s == &quot;Aaawww...&quot;){
            for(auto &amp;amp;c : teams[id].problems){
                if(c == &apos;P&apos;){
                    c = &apos;R&apos;;
                    break;
                }
            }
        }
        else{
            s.pop_back();
            s.pop_back();
            s.pop_back();
            s = s.substr(6);
            int up = s.size();
            for (auto &amp;amp;c : teams[id].problems) {
                if (c == &apos;P&apos;) {
                    c = &apos;A&apos;;
                    break;
                }
            }
            for(int i = id - 1; i &amp;gt;= id - up; i --){
                swap(teams[i], teams[i + 1]);
            }
        }
    }
    for(int i = 1;i &amp;lt;= n; i++){
        if(teams[i].id == p){
            cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &apos;\n&apos;;
            return;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/J&quot;&gt;J. Jumbled Scoreboards&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;判断给定的对是否形成&lt;strong&gt;不降&lt;/strong&gt;序&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;按题意模拟即可&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt; a(n + 1);
    for (int i = 1; i &amp;lt;= n; i++) {
        cin &amp;gt;&amp;gt; a[i].first &amp;gt;&amp;gt; a[i].second;
    }
    for (int i = 2; i &amp;lt;= n; i++) {
        if (a[i].second &amp;lt; a[i - 1].second || a[i].first &amp;lt; a[i - 1].first) {
            cout &amp;lt;&amp;lt; &quot;no\n&quot;;
            return;
        }
    }
    cout &amp;lt;&amp;lt; &quot;yes\n&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/G&quot;&gt;G. Grocery Greed&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定 $n$ 个价格的物品，可以选择现金或刷卡支付，若选择现金支付价格将四舍五入最接近的 $0.05$，你可以将多个物品分为一组进行支付，使总价格最小&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;将所有价格单位转化成美分，并对 $5$ 美分取模，这样就得到模数为 $0 - 4$ 美分的各个物品的价格&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于所有的 $1$ 和 $2$ 美分的价格直接向下取整&lt;/li&gt;
&lt;li&gt;对于每一对 $3$ 和 $4$ 美分的价格分为一组得到 $2$ 美分后向下取整&lt;/li&gt;
&lt;li&gt;若剩余 $3$，则将每一对 $3$ 分为一组得到 $1$ 后向下取整&lt;/li&gt;
&lt;li&gt;若剩余 $4$，则将每三个 $4$ 分为一组得到 $2$ 后向下取整&lt;/li&gt;
&lt;li&gt;剩余的价格直接加入答案&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;为什么要先把 $3$ 和 $4$ 组成一对，再处理剩余的？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;令 $f$ 表示将一些价格分为一组可以省下来的美分&lt;/p&gt;
&lt;p&gt;$$
f(3,4) = 2\
f(3 * 2) = 1\
f(4 * 3) = 2\
f(3 * 2, 4 * 2) = f(3 * 2) + f(4 * 3) = 4\
f(3 * 3, 4 * 3) = f(3 * 4) + f(4 * 3) = 4
$$&lt;/p&gt;
&lt;p&gt;显然对于省下同样的美分，将 $3$ 和 $4$ 组合起来一定比单独分组更优&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;int&amp;gt; a(5);
    int ans = 0;
    for (int i = 1; i &amp;lt;= n; i++) {
        double x;
        cin &amp;gt;&amp;gt; x;
        // use round() not (int)()
        int p = round(x * 100);
        a[p % 5]++;
        ans += p;
    }
    ans -= a[1];
    ans -= a[2] * 2;
    ans -= min(a[3], a[4]) * 2;
    int tmp = min(a[3], a[4]);
    a[3] -= tmp;
    a[4] -= tmp;
    ans -= a[3] / 2;
    ans -= a[4] / 3 * 2;
    cout &amp;lt;&amp;lt; fixed &amp;lt;&amp;lt; setprecision(2) &amp;lt;&amp;lt; ans / 100.00 &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/E&quot;&gt;E. Extraterrestrial Exploration&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定一个长度为 $n$ 的不递减的未知数列 $o$，每一次询问 $i$ 可以得到 $o_i$ 的值，在 $50$ 次询问内，得到一个三元组 $(x, y, z)(1 \leq x,y,z \leq n, x \neq y, x \neq z, y \neq z)$ 使得 $\sqrt{|{o_x - o_y}|} + \sqrt{|{o_x - o_z}|} + \sqrt{|{o_y - o_z}|}$ 最大&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;设 $x &amp;lt; y &amp;lt; z$，显然 $x = 1, z = n$ 必然最优，即：&lt;/p&gt;
&lt;p&gt;$$
\sqrt{|{o_y - o_1}|} + \sqrt{|{o_n - o_y}|} + \sqrt{|{o_1 - o_n}|}
$$&lt;/p&gt;
&lt;p&gt;我们只需要确定 $y$ 的值即可，该函数求导后为一个凸函数，在 $o_y = \frac{1}{2} (o_1 + o_n)$ 最大，二分找到原数列最接近的值即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int ask(int x) {
    cout &amp;lt;&amp;lt; &quot;? &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
    int res;
    cin &amp;gt;&amp;gt; res;
    return res;
}

void answer(int x, int y, int z) {
    cout &amp;lt;&amp;lt; &quot;! &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; z &amp;lt;&amp;lt; endl;
    return;
}
void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    int target = ask(1) + ask(n);
    int ans = -1;
    int res = INF;
    int l = 2, r = n - 1;
    while(l &amp;lt;= r){
        int mid = (l + r) &amp;gt;&amp;gt; 1;
        int tmp = ask(mid);
        if(abs(tmp * 2 - target) &amp;lt; res){
            ans = mid;
            res = abs(tmp * 2 - target);
        }
        if(tmp * 2 == target){
            break;
        }
        if(tmp * 2 &amp;lt; target){
            l = mid + 1;
        }else{
            r = mid - 1;
        }
    }
    answer(1, ans, n);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/I&quot;&gt;I. Interrail Pass&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;计划完成 $n$ 天的旅行，票价分为单程票和通行证&lt;/p&gt;
&lt;p&gt;$n$ 张单程票，第 $i$ 张单程票表示第 $t_i$ 天旅行票价为 $f_i$&lt;/p&gt;
&lt;p&gt;$k$张通行证，第 $i$ 张通行证表示购买价格为 $c_i$ 的通行证后，接下来的 $p$ 天内（包含当天），前 $d$ 次旅行免费&lt;/p&gt;
&lt;p&gt;求完成 $n$ 天旅行的最少花费的价格&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;令 $pre(t)$ 表示第 $t$ 天之前的最新的旅行日的索引，即 $max(i|t_i \leq t)$，这个可以预处理&lt;/p&gt;
&lt;p&gt;令 $dp_i$ 表示前 $i$ 次旅行最少花费的价格&lt;/p&gt;
&lt;p&gt;那么得到转移方程:&lt;/p&gt;
&lt;p&gt;$$
dp_i = \min
\begin{cases}
f_i + dp_{i-1}\
\min\limits_{1 \leq j \leq k}{c_j + dp_{\max(i-d_j, pre(t_i-p_j))}}
\end{cases}
$$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
    vector&amp;lt;int&amp;gt; t(n + 1), f(n + 1);
    for (int i = 1; i &amp;lt;= n; i++) {
        cin &amp;gt;&amp;gt; t[i] &amp;gt;&amp;gt; f[i];
    }
    vector&amp;lt;int&amp;gt; pre((int)1e6 + 5);
    int id = 0;
    for (int i = 0; i &amp;lt;= 1e6; i++) {
        while (id &amp;lt; n &amp;amp;&amp;amp; t[id + 1] &amp;lt;= i) {
            id++;
        }
        pre[i] = id;
    }
    vector&amp;lt;int&amp;gt; p(k + 1), d(k + 1), c(k + 1);
    for (int i = 1; i &amp;lt;= k; i++) {
        cin &amp;gt;&amp;gt; p[i] &amp;gt;&amp;gt; d[i] &amp;gt;&amp;gt; c[i];
    }
    auto get_pre = [&amp;amp;](int x) {
        if (x &amp;lt; 0) return 0;
        return pre[x];
    };
    vector&amp;lt;int&amp;gt; dp(n + 1);
    for (int i = 1; i &amp;lt;= n; i++) {
        dp[i] = dp[i - 1] + f[i];
        for (int j = 1; j &amp;lt;= k; j++) {
            dp[i] = min(c[j] + dp[max(i - d[j], get_pre(t[i] - p[j]))], dp[i]);
        }
    }
    cout &amp;lt;&amp;lt; dp[n] &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/F&quot;&gt;F. Failing Factory&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定 $n$ 个步骤，这些步骤有依赖关系（可能存在循环依赖），步骤 $i$ 有 $p_i$ 概率失败，一个步骤不失败要求它及其前置步骤均不失败，问失败概率最小的步骤的不失败概率为多少&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;对于非循环依赖的步骤，显然所有不存在依赖的步骤失败概率最小，对于循坏依赖的步骤，其组成的一个强连通分量中的每一个步骤的不失败概率均为 $\prod(1 - p_i)$ 先求强连通分量，再对所有入度为 $0$ 的步骤的失败概率取最小即可。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;/p&gt;
&lt;p&gt;输出小数点后200位&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const int N = 1e5 + 5;
int dfn[N], low[N], dfncnt, s[N], in_stack[N], tp;
int scc[N], sc;  // 结点 i 所在 SCC 的编号
int sz[N];       // 强连通 i 的大小
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; e(N);
void tarjan(int u) {
    low[u] = dfn[u] = ++dfncnt, s[++tp] = u, in_stack[u] = 1;
    for (auto v : e[u]) {
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (in_stack[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (dfn[u] == low[u]) {
        ++sc;
        do {
            scc[s[tp]] = sc;
            sz[sc]++;
            in_stack[s[tp]] = 0;
        } while (s[tp--] != u);
    }
}

void idol_produce(int testCase) {
    /*Code Here*/
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    vector&amp;lt;double&amp;gt; p(n + 1);
    for(int i = 1;i &amp;lt;= n; i++){
        double x;
        cin &amp;gt;&amp;gt; x;
        p[i] = 1.0 - x;
    }
    vector&amp;lt;pair&amp;lt;int,int&amp;gt; &amp;gt; ed;
    for (int i = 1; i &amp;lt;= m; i++) {
        int u, v;
        cin &amp;gt;&amp;gt; u &amp;gt;&amp;gt; v;
        e[v].push_back(u);
        ed.push_back({u, v});
    }
    for(int i = 1;i &amp;lt;= n; i++){
        if(!dfn[i]) tarjan(i);
    }
    vector&amp;lt;double&amp;gt; ans(sc + 1, 1);
    vector&amp;lt;int&amp;gt; in(sc + 1);
    for(int i = 1;i &amp;lt;= n; i++){
        ans[scc[i]] *= p[i];
    }
    for(auto &amp;amp;[u, v] : ed){
        if(scc[u] != scc[v]){
            in[scc[u]]++;
        }
    }
    double res = 0;
    for(int i = 1;i &amp;lt;= sc; i++){
        if(in[i] == 0){
            res = max(res, ans[i]);
        }
    }
    cout &amp;lt;&amp;lt; fixed &amp;lt;&amp;lt; setprecision(200) &amp;lt;&amp;lt; res &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/M&quot;&gt;M. Museum Visit&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定 $1-n$ 每个整数的代价 $c_i$, 选择一个整数集合使得对于共计 $m$ 个区间的每一个区间 $[l_i,r_i]$，都有至少一个整数落在区间内，求最小的代价之和&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;显然动态规划：&lt;/p&gt;
&lt;p&gt;设 $dp_i$ 表示在选择整数 $i$ 的前提下，所有 $r_i \leq i$ 的区间均被满足的最小代价&lt;/p&gt;
&lt;p&gt;$$
dp_i = c_i + \min\limits_{j \in [k_i,i)}dp_j
$$&lt;/p&gt;
&lt;p&gt;其中 $k_i$ 表示所有 $\max(l_i|r_i &amp;lt; i)$，即所有满足 $r_i &amp;lt; i$ 的区间中，最大的 $l_i$&lt;/p&gt;
&lt;p&gt;可以使用线段树维护 $dp$ 值&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    vector&amp;lt;ll&amp;gt; dp(n + 2, 0);
    vector&amp;lt;ll&amp;gt; c(n + 2, 0);
    for (int i = 1; i &amp;lt;= n; i++) {
        cin &amp;gt;&amp;gt; c[i];
    }
    vector&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; p(m + 1);
    for (int i = 1; i &amp;lt;= m; i++) {
        cin &amp;gt;&amp;gt; p[i].first &amp;gt;&amp;gt; p[i].second;
    }
    sort(p.begin() + 1, p.end(), [](const pair&amp;lt;int,int&amp;gt; &amp;amp;a, const pair&amp;lt;int,int&amp;gt; &amp;amp;b) {
        return a.second &amp;gt; b.second;
    });
    SegmentMinTree st(vector&amp;lt;ll&amp;gt;(n + 2));
    int l = 0;
    for(int i = 1;i &amp;lt;= n + 1; i++){
        while(!p.empty() &amp;amp;&amp;amp; p.back().second &amp;lt; i){
            l = max(l, p.back().first);
            p.pop_back();
        }
        if(l == 0) dp[i] = c[i];
        else dp[i] = c[i] + st.query(l, i - 1);
        st.change(i, i, dp[i]);
    }
    cout &amp;lt;&amp;lt; dp[n + 1] &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/K&quot;&gt;K. Karaoke Compression&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定一个字符串 $s$，选择一个非空字符串 $t$，将字符串 $s$ 中的所有不相交的子串 $t$ 均替换为单个字符，获得新的字符串 $s&apos;$，求 $|{t}| + |{s&apos;}|$ 的最小值&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;采用哈希计算每个不相交子字符串出现的数量，注意必须选择一个非空子串，所以初始 $ans = |{s}| + 1$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;/p&gt;
&lt;p&gt;不要用 map or unordered_map 将哈希值直接作为下标存储对应的 $index$ 集合，否则你的时间常数和空间常数会巨大&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int get_id(int i, int j){
    return 5000 * i + j;
}
void idol_produce(int testCase) {
    /*Code Here*/
    string s;
    cin &amp;gt;&amp;gt; s;
    int n = s.size();
    vector&amp;lt;ull&amp;gt; hash_value(5005 * 5005);
    for(int i = 0; i &amp;lt; n; i++){
        ull h = 0;
        for(int j = i; j &amp;lt; s.size(); j++){
            h = h * 131 + s[j];
            hash_value[get_id(i, j + 1)] = h;
        }
    }
    int ans = s.size() + 1;
    unordered_map&amp;lt;ull, int&amp;gt; count, next_pos;
    for(int len = 2; len &amp;lt; n; len++){
        count.clear();
        next_pos.clear();
        for(int i = 0; i &amp;lt;= n - len; i++){
            ull h = hash_value[get_id(i, i + len)];
            if(next_pos[h] &amp;gt; i) continue;
            count[h]++;
            next_pos[h] = i + len;
        }
        for(auto [_,c]:count){
            ans = min(ans, n - (len - 1) * (c - 1) + 1);
        }
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &quot;\n&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/B&quot;&gt;B. Buggy Blinkers&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;在未加权图中找到最短路径，但不能进行超过 $k$ 个不间断的转弯。求最短路径&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;以 &lt;strong&gt;路口位置，路口方向，信号灯状态，开启次数，步数&lt;/strong&gt; 为节点状态进行广度优先搜索。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;/p&gt;
&lt;p&gt;朝路口某段方向行驶时，不一定走直线。如样例，从路口 $2$ 向南行驶，到达路口 $3$ 时不处在其北侧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt; a(5005, vector&amp;lt;int&amp;gt;(4, 0));

enum Light { left = -1, right = 1, up = 0 };
struct node {
    int loc;
    int direction;
    int light;
    int time;
    int step;

    node go_up() {
        node res = *this;
        if (res.light != Light::up) {
            res.light = Light::up;
        }
        res.loc = a[res.loc][(res.direction + 2) % 4];
        res.step += 1;
        if (res.loc == 0) return res;
        for (int i = 0; i &amp;lt; 4; i++) {
            if (a[res.loc][i] == this-&amp;gt;loc) res.direction = i;
        }
        return res;
    }

    node go_right() {
        auto res = *this;
        if (res.light != Light::right) {
            res.light = Light::right;
            res.time += 1;
        }
        res.loc = a[res.loc][(res.direction + 3) % 4];
        res.step += 1;
        if (res.loc == 0) return res;
        for (int i = 0; i &amp;lt; 4; i++) {
            if (a[res.loc][i] == this-&amp;gt;loc) res.direction = i;
        }
        return res;
    }

    node go_left() {
        auto res = *this;
        if (res.light != Light::left) {
            res.light = Light::left;
            res.time += 1;
        }
        res.loc = a[res.loc][(res.direction + 1) % 4];
        res.step += 1;
        if (res.loc == 0) return res;
        for (int i = 0; i &amp;lt; 4; i++) {
            if (a[res.loc][i] == this-&amp;gt;loc) res.direction = i;
        }
        return res;
    }

    bool operator==(const node &amp;amp;x) const {
        return x.loc == loc &amp;amp;&amp;amp; x.direction == direction &amp;amp;&amp;amp; x.light == light &amp;amp;&amp;amp;
               x.time == time;
    }
};

struct NodeHasher {
    size_t operator()(const node &amp;amp;x) const {
        return (hash&amp;lt;int&amp;gt;()(x.loc) * 4) ^ (hash&amp;lt;int&amp;gt;()(x.direction) * 3) ^
               (hash&amp;lt;int&amp;gt;()(x.light) * 2) ^ hash&amp;lt;int&amp;gt;()(x.time);
    }
};
void idol_produce(int testCase) {
    /*Code Here*/
    int n, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
    for (int i = 1; i &amp;lt;= n; i++) {
        for (int j = 0; j &amp;lt; 4; j++) {
            cin &amp;gt;&amp;gt; a[i][j];
        }
    }
    if (n == 1) {
        cout &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; &apos;\n&apos;;
        return;
    }
    queue&amp;lt;node&amp;gt; q;
    for (int i = 0; i &amp;lt;= 3; i++) {
        if (a[1][i] == 0) continue;
        int dir = 0;
        for (int j = 0; j &amp;lt; 4; j++) {
            if (a[a[1][i]][j] == 1) {
                dir = j;
                break;
            }
        }
        q.push({a[1][i], dir, up, 0, 1});
    }
    unordered_set&amp;lt;node, NodeHasher&amp;gt; vis;
    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        if (t.time &amp;gt; k) continue;
        if (vis.count(t)) continue;
        vis.insert(t);
        if (t.loc == n) {
            cout &amp;lt;&amp;lt; t.step &amp;lt;&amp;lt; &apos;\n&apos;;
            return;
        }
        auto res = t.go_up();
        if (res.loc != 0) q.push(res);
        res = t.go_left();
        if (res.loc != 0) q.push(res);
        res = t.go_right();
        if (res.loc != 0) q.push(res);
    }
    cout &amp;lt;&amp;lt; &quot;impossible\n&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105492/problem/C&quot;&gt;C. Concurrent Contests&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;将 $n$ 个数 $[a_1,a_2,a_3...,a_n]$ 分为 $m$ 组，每组的价值为 $p_j$ 使得所有数均满足以下条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对于组别 $j$ 的总和 $sum_j = \sum\limits_{i \in j} a_i$&lt;/li&gt;
&lt;li&gt;属于组别 $j$ 的数字 $a_i$ ，满足 $\frac{a_i \times p_j}{sum_j} \geq \frac{a_i \times p_k}{sum_k + a_i},k \neq j$&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;对数字从大到小排序，以此将每个数字放在其占比最大的一个分组中。&lt;/p&gt;
&lt;p&gt;证明：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;对于当前选择的数字 $a_i$，显然选择价值最大的组为最优解，设其选择的组别为 $k$。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于已经分好组的数字 $a_j$，满足 $a_j \geq a_i$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于与 $a_i$ 同组的 $a_j$，在 $a_i$ 不加入 $k$ 前满足：&lt;/p&gt;
&lt;p&gt;$$
\frac{a_i \times p_k}{sum_k + a_i} \geq \frac{a_i \times p_{other}}{sum_{other} + a_i}
$$&lt;/p&gt;
&lt;p&gt;因此，必然满足：&lt;/p&gt;
&lt;p&gt;$$
\frac{a_j \times p_k}{sum_k + a_i} \geq \frac{a_j \times p_{other}}{sum_{other} + a_j}, a_j \geq a_i
$$&lt;/p&gt;
&lt;p&gt;因此 $a_j$ 移到其他组一定不优&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于与 $a_i$ 不同组的 $a_j$，在 $a_i$ 不加入前就已经选择好了最优组，$a_i$ 加入 $k$ 只是让一个对于 $a_j$ 不优的组变得更加不优而已&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    vector&amp;lt;pair&amp;lt;ll, int&amp;gt;&amp;gt; a(n + 1);
    vector&amp;lt;ll&amp;gt; p(m + 1);
    for (int i = 1; i &amp;lt;= n; i++) cin &amp;gt;&amp;gt; a[i].first, a[i].second = i;
    for (int i = 1; i &amp;lt;= m; i++) cin &amp;gt;&amp;gt; p[i];
    sort(a.begin() + 1, a.end(), greater&amp;lt;&amp;gt;());
    vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt; ans(m + 1);
    vector&amp;lt;ll&amp;gt; sum(m + 1);
    for(int i = 1;i &amp;lt;= n;i ++){
        int id = 1;
        for(int j = 2;j &amp;lt;= m;j ++){
            if(p[j] * (sum[id] + a[i].first) &amp;gt; p[id] * (sum[j] + a[i].first)){
                id = j;
            }
        }
        ans[id].emplace_back(a[i].second);
        sum[id] += a[i].first;
    }
    for(int i = 1;i &amp;lt;= m;i ++){
        cout &amp;lt;&amp;lt; ans[i].size() &amp;lt;&amp;lt; &apos; &apos;;
        for(int j = 0;j &amp;lt; ans[i].size();j ++){
            cout &amp;lt;&amp;lt; ans[i][j] &amp;lt;&amp;lt; &apos; &apos;;
        }
        cout &amp;lt;&amp;lt; &apos;\n&apos;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2024-12-13周赛 2024-2025 ICPC Northwestern European Regional Programming Contest</title><link>https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/2024-12-13%E5%91%A8%E8%B5%9B/</link><guid isPermaLink="true">https://fuyuki.fun/posts/%E9%A2%98%E8%A7%A3/2024-12-13%E5%91%A8%E8%B5%9B/</guid><description>2024-12-13周赛题解报告</description><pubDate>Thu, 12 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目链接&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://codeforces.com/gym/105562&quot;&gt;2024-2025 ICPC Northwestern European Regional Programming Contest (NWERC 2024)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/A&quot;&gt;A. Alphabetical Aristocrats&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定 $$n$$ 行字符串，从第一个大写字符开始计算有效字符，按字典序进行排序&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;签到，按题意模拟即可，注意在输入 $$n$$ 后，使用 $$cin.get()$$ 将第一行的回车读入缓存区&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    cin.get();
    vector&amp;lt;pair&amp;lt;string, string&amp;gt;&amp;gt; a(n + 1);
    for (int i = 1; i &amp;lt;= n; i++) {
        getline(cin, a[i].second);
        for (int j = 0; j &amp;lt; a[i].second.size(); j++) {
            if (a[i].second[j] &amp;gt;= &apos;A&apos; &amp;amp;&amp;amp; a[i].second[j] &amp;lt;= &apos;Z&apos;) {
                a[i].first = a[i].second.substr(j);
                break;
            }
        }
    }
    sort(a.begin() + 1, a.end());
    for (int i = 1; i &amp;lt;= n; i++) {
        cout &amp;lt;&amp;lt; a[i].second &amp;lt;&amp;lt; &apos;\n&apos;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/E&quot;&gt;E. Evolving Etymology&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定一个长度为 $$n$$ 的初始的字符串 $$s$$ ，按以下规则构造成一个新的字符串 $$s&apos;$$：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将两个 $$s$$ 合成一个字符串 $$t$$ 即：$$t = s + s$$&lt;/li&gt;
&lt;li&gt;$$s&apos;$$ 为 $$t$$ 所有的偶数下标的字符集，即 $$s&apos;=[t_0, t_2...t_{2n}]$$&lt;/li&gt;
&lt;li&gt;$$s:=s&apos;$$&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;重复 $$k$$ 次过程，输出最后的 $$s$$&lt;/p&gt;
&lt;h3&gt;数据范围&lt;/h3&gt;
&lt;p&gt;$$1 \le n \le 10^5$$, $$1 \le k \le 10^{18}$$&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;第一次过程获得的字符串为：$$[s_{0%n}, s_{2%n}, s_{4%n}, s_{6%n}...s_{2*(n-1)%n}]$$&lt;/p&gt;
&lt;p&gt;第二次过程获得的字符串为：$$[s_{0%n}, s_{4%n}, s_{8%n}, s_{12%n}...s_{4*(n-1)%n}]$$&lt;/p&gt;
&lt;p&gt;第三次过程获得的字符串为：$$[s_{0%n}, s_{8%n}, s_{16%n}, s_{24%n}...s_{8*(n-1)%n}]$$&lt;/p&gt;
&lt;p&gt;因此，第 $$k$$ 次过程获得的字符串为：$$\sum\limits_{i = 0} ^ {n-1} {s_{i * 2^k % n}}$$&lt;/p&gt;
&lt;p&gt;总时间复杂度为 $$O(n*log{k})$$，若预处理 $$i * 2 ^ k$$ 将优化到 $$O(n + logk)$$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    long long n, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
    mod = n;
    string s;
    cin &amp;gt;&amp;gt; s;
    string tmp;
    for(int i = 0; i &amp;lt; s.size(); i++){
        tmp += s[qpow(2, k) * i % n];
    }
    cout &amp;lt;&amp;lt; tmp &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/J&quot;&gt;J. Jib Job&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定 $$n$$ 个不同位置和高度的起重机塔。在每台起重机顶部放置一个吊臂，使得：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;任何臂架都不能与任何其他起重机塔碰撞&lt;/li&gt;
&lt;li&gt;臂架不能超过起重机的高度，且为整数长度&lt;/li&gt;
&lt;li&gt;使可达的面积最大&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;数据范围&lt;/h3&gt;
&lt;p&gt;$$ 1 \le n \le 500$$&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;每个起重机的最大臂长为所有与比它高的起重机的距离以及它本身高度的最小值&lt;/p&gt;
&lt;p&gt;由于 $$n \le 500$$，所以直接模拟即可&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;int&amp;gt;x(n + 1), y(n + 1), h(n + 1);
    for(int i = 1;i &amp;lt;= n;i++){
        cin &amp;gt;&amp;gt; x[i] &amp;gt;&amp;gt; y[i] &amp;gt;&amp;gt; h[i];
    }
    for(int i = 1;i &amp;lt;= n; i++){
        int ans = h[i];
        for(int j = 1; j &amp;lt;= n; j++){
            if(h[j] &amp;lt;= h[i]){
                continue;
            }
            ans = min(ans, (int)sqrt(pow(x[i] - x[j],2) + pow(y[i] - y[j], 2)));
        }
        cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/D&quot;&gt;D. Dutch Democracy&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定 $$n$$ 个数字，计所有满足以条件的子集的数目：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;子集数字之和严格大于总和的一半&lt;/li&gt;
&lt;li&gt;去掉子集的最小值后子集之和小于等于总和的一半&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;数据范围&lt;/h3&gt;
&lt;p&gt;$$1 \le n \le 60$$，$$1 \le a_i \le 10,000$$&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;从大到小dp每一个可能的和的子集数量，转移方程为：&lt;/p&gt;
&lt;p&gt;$$
dp[j] =
\begin{cases}
dp[j] + dp[j - a[i]], &amp;amp; \text{if } j &amp;gt; a[i] \
dp[0] + 1, &amp;amp; \text{if } j = a[i]
\end{cases}
$$&lt;/p&gt;
&lt;p&gt;由于是从大到小dp，此时所有新增的子集的最小值一定是 $a_i$，那么根据题意更新答案即可&lt;/p&gt;
&lt;p&gt;时间复杂度为 $$O(n * sum)$$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;long long&amp;gt; a(n);
    long long sum = 0;
    long long ans = 0;
    for (int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; a[i];
        sum += a[i];
    }
    sort(a.begin(), a.end(), greater&amp;lt;long long&amp;gt;());
    vector&amp;lt;ll&amp;gt; f(sum + 1);
    long long half = sum / 2;
    for (int i = 0; i &amp;lt; n; i++) {
        for (int j = half; j &amp;gt;= 0; j--) {
            if (f[j] &amp;gt; 0) {
                f[j + a[i]] += f[j];
                if (j + a[i] &amp;gt; half) {
                    ans += f[j];
                }
            }
        }
        f[a[i]] += 1;
        if (a[i] &amp;gt; half) {
            ans += 1;
        }
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/L&quot;&gt;L. Limited Library&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;有 $$n$$ 个书架，$$m$$ 本书，书架和书都有各自的高度，书只能放在比它高的书架里。&lt;/p&gt;
&lt;p&gt;如果一个书架放满书，那么最多放 $$x$$ 本书，如果一个书架放艺术品，那么最多放 $$y$$ 本书&lt;/p&gt;
&lt;p&gt;求保证书全放在书架的情况下，最多放几个书架的艺术品&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;在确定放几件艺术品的情况下，显然让最矮的书架放艺术品更优，所以二分放艺术品的书架数量即可&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, m, x, y;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y;
    vector&amp;lt;int&amp;gt; a(n + 1);
    for (int i = 1; i &amp;lt;= n; i++) {
        cin &amp;gt;&amp;gt; a[i];
    }
    vector&amp;lt;int&amp;gt; b(m + 2);
    for (int i = 1; i &amp;lt;= m; i++) {
        cin &amp;gt;&amp;gt; b[i];
    }
    b[m + 1] = INF;
    int l = 0, r = n;
    int ans = -1;
    sort(a.begin() + 1, a.end());
    sort(b.begin() + 1, b.end());
    auto check = [&amp;amp;](int mid) {
        int book = 1;
        for (int i = 1; i &amp;lt;= mid; i++) {
            for (int j = 1; j &amp;lt;= y; j++) {
                if (a[i] &amp;gt;= b[book]) {
                    book++;
                }
                if (book == m + 1) {
                    return true;
                }
            }
        }
        for (int i = mid + 1; i &amp;lt;= n; i++) {
            for (int j = 1; j &amp;lt;= x; j++) {
                if (a[i] &amp;gt;= b[book]) {
                    book++;
                }
                if (book == m + 1) {
                    return true;
                }
            }
        }
        return false;
    };
    while (l &amp;lt;= r) {
        int mid = (l + r) &amp;gt;&amp;gt; 1;
        if (check(mid)) {
            ans = mid;
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }
    if (ans == -1) {
        cout &amp;lt;&amp;lt; &quot;impossible\n&quot;;
    } else {
        cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/F&quot;&gt;F. Flowing Fountain&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;模拟香槟塔的倒入流程&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;用单调栈来初始化每一层塔所对应的下一层塔的位置，用并查集实现倒入流程的路径压缩&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n, q;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; q;
    vector&amp;lt;ll&amp;gt; siz(n + 2);
    vector&amp;lt;ll&amp;gt; val(n + 2);
    vector&amp;lt;ll&amp;gt; nex(n + 2);
    for (int i = 1; i &amp;lt;= n; i++) {
        cin &amp;gt;&amp;gt; siz[i];
        val[i] = 0;
    }
    siz[n + 1] = 1e18;
    vector&amp;lt;int&amp;gt; p;
    for (int i = 1; i &amp;lt;= n + 1; i++) {
        while (!p.empty() &amp;amp;&amp;amp; siz[i] &amp;gt; siz[p.back()]) {
            nex[p.back()] = i;
            p.pop_back();
        }
        p.emplace_back(i);
    }
    auto get_next = [&amp;amp;](int x, auto&amp;amp; get_next) {
        if (val[nex[x]] &amp;lt; siz[nex[x]]) {
            return nex[x];
        } else {
            return nex[x] = get_next(nex[x], get_next);
        }
    };
    while (q--) {
        char op;
        cin &amp;gt;&amp;gt; op;
        if (op == &apos;+&apos;) {
            ll l, x;
            cin &amp;gt;&amp;gt; l &amp;gt;&amp;gt; x;
            if (val[l] + x &amp;lt;= siz[l]) {
                val[l] += x;
            } else {
                x -= siz[l] - val[l];
                val[l] = siz[l];
                while (x) {
                    l = get_next(l, get_next);
                    ll nextval = val[l] + x;
                    if (nextval &amp;lt;= siz[l]) {
                        val[l] = nextval;
                        break;
                    } else {
                        x -= siz[l] - val[l];
                        val[l] = siz[l];
                    }
                }
            }
        }
        else{
            int x;
            cin &amp;gt;&amp;gt; x;
            cout &amp;lt;&amp;lt; val[x] &amp;lt;&amp;lt; &apos;\n&apos;;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/H&quot;&gt;H. Hash Collision&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;给定一个范围 $$n$$ 和一个散列函数 $$f$$，这个散列函数接受两个 $$1-n$$ 的参数 $$c$$, $$r$$, $$f^c(r)$$ 表示将初始值 $$r$$ 进行 $$c$$ 次散列化，并返回一个范围为 $$1-n$$ 的散列值，在 $$1000$$ 次询问内找到一对 $$c$$, $$r$$ 满足 $$f^c(r) = c$$&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;询问 $$n$$, $$c$$，得到 $$x = f^n(c)$$&lt;/li&gt;
&lt;li&gt;如果 $$x=n$$，那么就找到一对答案 $$f^n(c) = n$$&lt;/li&gt;
&lt;li&gt;否则必然满足 $$x &amp;lt; n$$，询问 $$n-x, c$$，得到 $$y = f^{n-x}(c)$$&lt;/li&gt;
&lt;li&gt;此时满足 $$f^x(y) = f^x(f^{n-x}(c)) = f^n(c) = x$$&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    cout &amp;lt;&amp;lt; &quot;? &quot; &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; 1 &amp;lt;&amp;lt; endl;
    int x;
    // f(n, 1)
    cin &amp;gt;&amp;gt; x;
    if (x == n) {
        cout &amp;lt;&amp;lt; &quot;! &quot; &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; 1 &amp;lt;&amp;lt; endl;
        return;
    }
    cout &amp;lt;&amp;lt; &quot;? &quot; &amp;lt;&amp;lt; n - x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; 1 &amp;lt;&amp;lt; endl;
    // f(n - x, 1)
    int y;
    cin &amp;gt;&amp;gt; y;
    // f(x, f(n - x, 1)) = f(n, 1) = x
    // f(x, y) = x
    cout &amp;lt;&amp;lt; &quot;! &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; y &amp;lt;&amp;lt; endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/K&quot;&gt;K. Kruidnoten&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;在无向图中，其中一些点有 $$p_i$$ 的概率成为途径点，每个点之间的概率独立计算，问在至少经过一个途径点的情况下的期望最短路径是多少&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;当选择其中一个点作为路径上的途径点时最优时，必然满足经过其他途径点的且比这条路径更短的路径上的途径点都在 $$1 - p_j$$ 的概率下未能成为途径点，且这个点在 $$p_i$$ 的概率下成为了途径点&lt;/p&gt;
&lt;p&gt;故使用双向Dijkstra算法求出每个途径点所在的最短路径，并排序。&lt;/p&gt;
&lt;p&gt;得到 $$dis(v_1) &amp;lt; dis(v_2) &amp;lt; dis(v_3) ... dis(v_n)$$&lt;/p&gt;
&lt;p&gt;答案为 $$ans = \sum\limits_{i = 1}^{n} p_{v_i} * \prod\limits_{j = 1}^{n-1}(1-p_{v_j})$$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;constexpr int MAXN = 1e6 + 100;
struct edge {
    ll to, weight;
};
struct node {
    ll id, weight;
    bool operator&amp;lt;(const node&amp;amp; rhs) const { return weight &amp;gt; rhs.weight; }
};
vector&amp;lt;edge&amp;gt; e[MAXN];

vector&amp;lt;ll&amp;gt; dijkstra(int s) {
    vector&amp;lt;ll&amp;gt; dis(MAXN, 1e18);
    vector&amp;lt;bool&amp;gt; vis(MAXN, false);
    dis[s] = 0;
    priority_queue&amp;lt;node&amp;gt; q;
    q.push({s, 0});
    while (!q.empty()) {
        auto [u, d] = q.top();
        q.pop();
        if (vis[u]) continue;
        vis[u] = true;
        for (auto [v, w] : e[u]) {
            if (dis[v] &amp;gt; d + w) {
                dis[v] = d + w;
                q.push({v, dis[v]});
            }
        }
    }
    return dis;
}

struct reslut {
    ll dis, id;
    long double p;
    bool operator&amp;lt;(const reslut&amp;amp; rhs) const { return dis &amp;lt; rhs.dis; }
};
void idol_produce(int testCase) {
    /*Code Here*/
    int n, m, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;
    vector&amp;lt;reslut&amp;gt;res(k + 1);
    for (int i = 1; i &amp;lt;= m; i++) {
        ll u, v, w;
        cin &amp;gt;&amp;gt; u &amp;gt;&amp;gt; v &amp;gt;&amp;gt; w;
        e[u].push_back({v, w});
        e[v].push_back({u, w});
    }
    for (int i = 1; i &amp;lt;= k; i++) {
        cin &amp;gt;&amp;gt; res[i].id &amp;gt;&amp;gt; res[i].p;
    }
    if (!any_of(res.begin() + 1, res.end(), [](const reslut&amp;amp; x) { return x.p == 1; })){
        cout &amp;lt;&amp;lt; &quot;impossible\n&quot;;
        return;
    }
    auto dis = dijkstra(1);
    for(int i = 1; i &amp;lt;= k; i++){
        res[i].dis = dis[res[i].id];
    }
    dis = dijkstra(n);
    for(int i = 1; i &amp;lt;= k; i++){
        res[i].dis += dis[res[i].id];
    }
    sort(res.begin() + 1, res.end());
    long double ans = 0;
    long double pre = 1;
    for(int i = 1;i &amp;lt;= k; i++){
        ans += res[i].dis * pre * res[i].p;
        pre *= (1.0 - res[i].p);
    }
    cout &amp;lt;&amp;lt; fixed &amp;lt;&amp;lt; setprecision(10) &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/C&quot;&gt;C. Connect Five&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;矩阵上有5个点，用最短的路径连接这5个点满足每个点之间的路径长度最短&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;注意到，对于最左侧的点，必然有一条直线向右的路径直到与另一个点在同一条直线上。因此可以将最左侧的点向右平移，这对于上下左右方向都同理。&lt;/p&gt;
&lt;p&gt;如果操作后使得多个点在同一个位置，那么合并这多个点，因为这多个点接下来一定共享接下来的路径&lt;/p&gt;
&lt;p&gt;重复操作，直到没有点可以缩短和合并&lt;/p&gt;
&lt;p&gt;所有操作完成后，剩下的点集必然构成一个点，一个矩阵，或两个相邻矩阵，根据不同的结果进行划分即可。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void idol_produce(int testCase) {
    /*Code Here*/
    vector&amp;lt;pair&amp;lt;ll, ll&amp;gt; &amp;gt; a(5);
    for (int i = 0; i &amp;lt; 5; i++) {
        cin &amp;gt;&amp;gt; a[i].first &amp;gt;&amp;gt; a[i].second;
    }
    ll ans = 0;
    for (int i = 1; i &amp;lt;= 100 &amp;amp;&amp;amp; a.size() &amp;gt; 1; i++) {
        sort(a.begin(), a.end(), [](pair&amp;lt;int, int&amp;gt; x, pair&amp;lt;int, int&amp;gt; y) {
            if(x.first == y.first){
                return x.second &amp;lt; y.second;
            }
            return x.first &amp;lt; y.first;
        });
        ans += a[1].first - a[0].first;
        a[0].first = a[1].first;
        ans += a[a.size() - 1].first - a[a.size() - 2].first;
        a[a.size() - 1].first = a[a.size() - 2].first;
        sort(a.begin(), a.end(), [](pair&amp;lt;int, int&amp;gt; x, pair&amp;lt;int, int&amp;gt; y) {
            if(x.second == y.second){
                return x.first &amp;lt; y.first;
            }
            return x.second &amp;lt; y.second;
        });
        ans += a[1].second - a[0].second;
        a[0].second = a[1].second;
        ans += a[a.size() - 1].second - a[a.size() - 2].second;
        a[a.size() - 1].second = a[a.size() - 2].second;
        sort(a.begin(), a.end());
        a.erase(unique(a.begin(), a.end()), a.end());
    }
    if(a.size() == 1){
        cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
        return;
    }
    assert(a.size() &amp;gt;= 4);
    set&amp;lt;int&amp;gt;s1, s2;
    for(int i = 0; i &amp;lt; a.size(); i++){
        s1.insert(a[i].first);
        s2.insert(a[i].second);
    }
    assert(s1.size() &amp;lt;= 3 &amp;amp;&amp;amp; s2.size() &amp;lt;= 3);
    assert(s1.size() &amp;gt; 1 &amp;amp;&amp;amp; s2.size() &amp;gt; 1);
    if(s1.size() &amp;lt;= 2 || s2.size() &amp;lt;= 2){
        ans += 2 * (*s1.rbegin() - *s1.begin());
        ans += 2 * (*s2.rbegin() - *s2.begin());
        cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
        return;
    }
    else{
        ans += 2 * (*s1.rbegin() - *s1.begin());
        ans += 2 * (*s2.rbegin() - *s2.begin());
        int l1 = *s1.begin(), r1 = *s1.rbegin(), l2 = *s2.begin(), r2 = *s2.rbegin();
        for(int i = 0;i &amp;lt; a.size(); i++){
            if(a[i].first == l1 || a[i].first == r1 || a[i].second == l2 || a[i].second == r2){
                continue;
            }
            ans += min(*s1.rbegin() - *s1.begin(), *s2.rbegin() - *s2.begin());
            break;
        }
        cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
        return;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href=&quot;https://codeforces.com/gym/105562/problem/M&quot;&gt;M. Mouse Trap&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;凸多边形中任意三个点会组成一个三角形覆盖凸多边形的一部分区域，求凸多边形内部的一个点期望被多少个三角形覆盖&lt;/p&gt;
&lt;h3&gt;题解&lt;/h3&gt;
&lt;p&gt;题目可以直接转化成求凸多边形内所有的三元组点构成的三角形面积之和与凸多边形的面积的除数&lt;/p&gt;
&lt;p&gt;重点在于如何求出所有的三角形面积之和，我们转化公式：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\sum_{i &amp;lt; j &amp;lt; k}\frac{1}{2}(p_j - p_i) \otimes (p_k - p_j) \newline
= \frac{1}{2}\sum_j(\sum_{i &amp;lt; j}(p_j - p_i)) \otimes (\sum_{j &amp;lt; k}(p_k - p_j)) \newline
= \frac{1}{2}\sum_j((j-1)p_j - \sum_{i&amp;lt;j}p_i) \otimes (\sum_{j&amp;lt;k}p_k-(n-j)p_j)
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct point{
    double x, y;

    constexpr double operator^(point &amp;amp; rhs) const{
        return x * rhs.y - y * rhs.x;
    }
};
void idol_produce(int testCase) {
    /*Code Here*/
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;point&amp;gt; p(n + 10);
    vector&amp;lt;double&amp;gt; prex(n + 10), prey(n + 10), sufx(n + 10), sufy(n + 10);
    for (int i = 1; i &amp;lt;= n; i++) {
        cin &amp;gt;&amp;gt; p[i].x &amp;gt;&amp;gt; p[i].y;
    }
    prex[1] = p[1].x;
    sufx[n] = p[n].x;
    for (int i = 2; i &amp;lt;= n; i++) {
        prex[i] = prex[i - 1] + p[i].x;
    }
    for (int i = n - 1; i &amp;gt;= 1; i--) {
        sufx[i] = sufx[i + 1] + p[i].x;
    }
    prey[1] = p[1].y;
    sufy[n] = p[n].y;
    for (int i = 2; i &amp;lt;= n; i++) {
        prey[i] = prey[i - 1] + p[i].y;
    }
    for (int i = n - 1; i &amp;gt;= 1; i--) {
        sufy[i] = sufy[i + 1] + p[i].y;
    }
    auto get_area = [&amp;amp;]() -&amp;gt; double{
        double ans = 0;
        for(int i = 1;i &amp;lt;= n; i ++){
            int nxt = (i + 1);
            if (i == n) nxt = 1;
            ans += (p[i] ^ p[nxt]);
        }
        return abs(ans) / 2;
    };
    double area = get_area();
    double ans = 0;
    for(int i = 1;i &amp;lt;= n; i++){
        point a, b;
        a.x = (i - 1) * p[i].x - prex[i - 1];
        a.y = (i - 1) * p[i].y - prey[i - 1];
        b.x = sufx[i + 1] - (n - i) * p[i].x;
        b.y = sufy[i + 1] - (n - i) * p[i].y;
        ans += (a ^ b) / 2;
    }
    cout &amp;lt;&amp;lt; fixed &amp;lt;&amp;lt; setprecision(10) &amp;lt;&amp;lt; ans / area &amp;lt;&amp;lt; &apos;\n&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>