闲话 23.3.1

闲话

所以 cd 为啥突然学 gf
然后整出来一大堆奇怪的(初学者可能会有的)问题
建议来问我(
我还能乐呵乐呵(

EI:那你以后要知道这不奇怪
EI:小学生在初学负数时也会有疑惑

今日推歌:REWRITER - 八王子P feat. GUMI

杂题

我担心今天只有这一道了(

[HNOI2016]序列

给定一个长为 \(n\) 的序列 \(a\),记 \(a\) 的下标落在区间 \([l, r]\) 内的元素组成的序列为 \(a[l:r]\)

\(q\) 次询问,每次给定 \(l, r\),求 \(a[l:r]\) 的所有连续子序列最小值的和。

\(n, q\le 10^5, |a_i|\le 10^9\)

这题让我充分意识到我不会莫队变式了。

我们发现,题目要求的信息是容易由 \([l, r]\) 得到 \([l, r + 1]\) 的。这里只说右端点递增的情况,另三种移动端点的策略类似。

考虑当右端点递增时,我们只需要考虑 \(\forall k\in [l, r + 1],\ \min\{a[k:r + 1]\}\) 的贡献。也就是说,贡献区间的右端点是固定的。
不难想到,这贡献随 \(k\) 递减而减小,并且若 \(p = \text{arg min}_{i = l}^r a_i\),则 \(k\in [l, p]\) 时贡献都为 \(a_p\)。则我们求出 \(p\)\([l, p]\) 段的贡献就是 \((p - l + 1) \times a_p\),我们之后只需要观察递减的 \(k\in [p + 1, r + 1]\) 段。
这段的贡献是好求的。可以知道的是,若对于一个区间 \([l_0, r_0]\subseteq [p + 1, r + 1]\),若 \(r_0 = \text{arg min}_{i = l_0}^{r_0} a_i\),则这一段内某个位置作为左端点的贡献对任意的 \(r + 1\) 都是相同的。也就是说,我们预处理一个 \(\text{lm}[i]\) 表示 \(i\) 左侧第一个小于 \(a_i\) 的值,则当计算 \(p\le \text{lm}[i] < i\le r + 1\) 的答案时,这一段可以对答案贡献 \((i - \text{lm}[i]) \times a_i\)。预处理这个的前缀和即可。
因此我们就将一段区间拆解为了两段贡献,他们都是可以在 \(O(1)\) 得到的。

因此作莫队,可以得到 \(O(n\sqrt n)\) 的做法。

之后再补在线做法吧(

code
#include <bits/stdc++.h>
using namespace std;
using ll = long long; 
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 2e5 + 10;
signed main() {
    static int n, q, a[N], st[21][N], lgv[N];

    cin >> n >> q;
    rep(i,1,n) cin >> a[i], st[0][i] = i;
    rep(i,2,n) lgv[i] = lgv[i >> 1] + 1;
    rep(i,1,lgv[n]) rep(j,1,n - (1 << i) + 1) {
        int u = st[i - 1][j], v = st[i - 1][j + (1 << i - 1)];
        st[i][j] = a[u] < a[v] ? u : v;
    }
    auto getmin = [&](int l, int r) {
        int d = lgv[r - l + 1];
        l = st[d][l], r = st[d][r - (1 << d) + 1];
        return a[l] < a[r] ? l : r;
    };
    
    static int lm[N], rm[N], stk[N], top;
    static ll lsum[N], rsum[N];

    a[0] = - 1.5e9;
    stk[top = 1] = 0;
    rep(i,1,n) {
        while (top and a[stk[top]] >= a[i]) -- top;
        lm[i] = stk[top]; stk[++ top] = i;
    }
    a[0] = 0;
    a[n + 1] = - 1.5e9;
    stk[top = 1] = n + 1;
    pre(i,n,1) {
        while (top and a[stk[top]] >= a[i]) -- top;
        rm[i] = stk[top]; stk[++ top] = i;
    }
    rep(i,1,n) lsum[i] = lsum[lm[i]] + 1ll * (i - lm[i]) * a[i];
    pre(i,n,1) rsum[i] = rsum[rm[i]] + 1ll * (rm[i] - i) * a[i];

    static int sqv = sqrt(n) + 5, bel[N];
    static ll ans[N], now = 0;
    static struct queries {
        int l, r, id;
        bool operator < (const queries& b) const {
            if (bel[l] == bel[b.l]) {
                if (bel[l] & 1) return r < b.r;
                else return r > b.r;
            } return l < b.l;
        }
    } qy[N];
    auto movel = [&](int l, int r) {
        int pos = getmin(l, r);
        return rsum[l] - rsum[pos] + 1ll * (r - pos + 1) * a[pos]; 
    };
    auto mover = [&](int l, int r) {
        int pos = getmin(l, r);
        return lsum[r] - lsum[pos] + 1ll * (pos - l + 1) * a[pos];
    };

    rep(i,1,q) cin >> qy[i].l >> qy[i].r, qy[i].id = i;
    rep(i,1,n) bel[i] = (i - 1) / sqv + 1;
    sort(qy + 1, qy + 1 + q);
    for (int i = 1, l = 1, r = 0; i <= q; ++ i) {
        while (qy[i].l < l) now += movel(-- l, r);
        while (r < qy[i].r) now += mover(l, ++ r);
        while (l < qy[i].l) now -= movel(l ++, r);
        while (qy[i].r < r) now -= mover(l, r --);
        ans[qy[i].id] = now;
    } 
    
    rep(i,1,q) cout << ans[i] << '\n';
}



ABC278Ex

一个非负整数序列 \(S\) 是好的,当且仅当 \(S\) 存在一个非空子序列 \(T\),满足 \(T\) 中所有元素的异或和为 \(1\)

有一个初始为空的序列 \(A\),以及 \(2^m\) 张写着数字的卡片;卡片上的数字取遍 \([0, 2^m)\) 中的整数。你可以自由选择一张卡片,将这张卡片上的数字放在 \(A\) 序列的末尾,并删除这张卡片,以后不能再选择它。你会一直进行这个操作,当 \(A\) 成为好的序列后停止。

给定 \(n, m\),求停止操作时长度为 \(n\) 的不同 \(A\) 序列数。答案对 \(998244353\) 取模。

\(1\le n\le 2\times 10^5, \ 1\le m \le 10^7,\ n\le 2^m\)

想装b 去选金牌题做 然后发现哪个都不可做就跑路了 找了个这个题
增加水题提交数(

前置知识

题目中的生成方式等价于钦定序列 \(S\) 中数字互异。

\(A_n\) 是长度为 \(n\) 的好序列数量(数字不必两两不同),则答案即为 \(A_n - A_{n - 1} \times (2^m - n + 1)\)。这转化使我们可以简单容斥,用长度为 \(n\) 的序列总数减去数字互异的坏序列数就是答案。下面只需要计算数字互异的坏序列数即可。

\(f(n)\) 为数字互异的坏序列数,\(g(n)\) 为坏序列数。由于数字顺序对性质无影响,我们可以取一个数字互异的坏序列,将其中第 \(i\) 个元素扩增 \(k_i\ge 1\) 倍得到一个一般的坏序列。可以将每个数字看做一个盒子,第 \(i\) 个盒子中放 \(k_i\) 个球。这的组合意义自然导出了如下公式:

\[g(n) = \sum_{i = 0}^n \begin{Bmatrix} n \\ i \end{Bmatrix} f(i) \]

斯特林反演得到

\[f(n) = \sum_{i = 0}^n (-1)^{n - i} \begin{bmatrix} n \\ i \end{bmatrix} g(i) \]

我们已经充分了解了如何 \(O(n\log n)\) 求得一行斯特林数,下面只需要求得每个 \(g(k)\) 即可。这问题即为 CF1603F\(x > 0\) 时的情况。我们已经知道了

\[\begin{aligned} g(k) &= \sum_{r=0}^k\prod_{i=1}^r(2^m-2^i) \begin{bmatrix} k \\ r \end{bmatrix}_2 \\ & = \sum_{r=0}^k 2^{r (r + 1) / 2} [m - 1]_2^{\underline {r - 1}} \begin{bmatrix} k \\ r \end{bmatrix}_2 \\ & = \sum_{r=0}^k 2^{r (r + 1) / 2} \frac{[m - 1]_2!}{[m - 1 - r]_2!} \begin{bmatrix} k \\ r \end{bmatrix}_2 \\ & = [m - 1]_2! [k]_2!\sum_{r=0}^k \left( \frac{2^{r (r + 1) / 2}}{[m - 1 - r]_2![r]_2! }\right)\left( \frac{1}{[k - r]_2!} \right) \end{aligned}\]

一次卷积即可。

总时间复杂度为 \(O(n\log n + m)\),需要预处理 \(\text{q-}\)阶乘和它的逆元。

Submission.



ABC279Ex

给定 \(n, m\),保证 \(n\le m\le 2n\)。设 \(a\) 枚举长度为 \(n\) 且满足 \(\sum_{i = 1}^n a_i = m\) 的所有序列 \(a\),求

\[\sum_{a} \prod_{i = 1}^n \min(i, a_i) \]

\(1\le n\le 10^{12}\)

看到经典的枚举序列的形式,我们不妨考虑枚举第 \(i\) 个元素取值为 \(a_i\) 时的贡献,并写成 gf 形式,最后乘起来提取 \(x^m\) 项系数即可。

考虑 \(F_k(x) = x + 2x^2 + 3x^3 + \cdots + kx^k + kx^{k + 1} + kx^{k + 2} + \cdots\),我们知道这就是第 \(i\) 个元素的贡献,而答案就是 \([x^m] \prod_k F_k(x)\)。我们首先要得到 \(F_k\) 的封闭形式。
我反正是暴力推导的。

\[\begin{aligned} F_k(x) &= x + 2x^2 + 3x^3 + \cdots + kx^k + kx^{k + 1} + kx^{k + 2} + \cdots \\ & = \sum_{i = 1}^{k - 1} ix^i + k\sum_{i\ge k} x^i \\ & = x\sum_{i = 0}^{k - 2} (i + 1)x^i + kx^k\sum_{i\ge 0} x^i \\ & = x \left(\sum_{i = 0}^{k - 2} x^{i + 1}\right)' + kx^k \frac{1}{1 - x} \\ & = x \left(\frac{x(1 - x^{k - 1})}{1 - x}\right)' + \frac{kx^k}{1 - x} \\ & = \frac{(k - 1) x^{k + 1} - k x^k + x}{(1 - x)^2} + \frac{kx^k}{1 - x} \\ & = \frac{kx^{k + 1} - x^{k + 1} - k x^k + x + kx^k - kx^{k + 1}}{(1 - x)^2} \\ & = \frac{x - x^{k + 1}}{(1 - x)^2} = \frac{x(1 - x^{k})}{(1 - x)^2} \end{aligned}\]

题解给出了一种更妙的做法:注意到 \(F_k(x) (1 - x) = x + x^2 + x^3 + \cdots + x^k\),进而 \(F_k(x) (1 - x)^2 = x - x^{k + 1}\),我们能得到 \(F_k(x) = \dfrac{x(1 - x^k)}{(1 - x)^2}\)

那答案就是

\[[x^m] \prod_{k = 1}^n \frac{x(1 - x^{k})}{(1 - x)^2} = [x^{m - n}] (1 - x)^{-2n} \prod_{k = 1}^n (1 - x^{k}) \]

注意到 \(0\le m - n\le n\),因此在提取系数的过程中 \(\prod_{k = 1}^n (1 - x^{k})\) 等价于 \(\prod_{k = 1}^{\infty} (1 - x^{k})\),而后者就是欧拉函数的生成函数。五边形数定理是广为人知的,它指出了

\[\prod_{k = 1}^{\infty} (1 - x^{k}) = \sum_{i\ge 0} (-1)^i x^{\frac{i(3i\pm 1)}2} \]

因此在整个过程中,\(\prod_{k = 1}^n (1 - x^{k})\) 只有 \(O(\sqrt n)\) 个位置有值,我们将他记作 \(\sum_{i = 0}^k f_i x^{a_i}\)\(k = O(\sqrt n)\)\(f_i\neq 0\)

这样我们就可以自然地展开原式了。

\[\begin{aligned} & [x^{m - n}] (1 - x)^{-2n} \prod_{k = 1}^n (1 - x^{k}) \\ = \ & [x^{m - n}] \left(\sum_{i\ge 0} \binom{i + 2n - 1}{i} x^i \right) \left(\sum_{i = 0}^k f_i x^{a_i}\right) \\ = \ & \sum_{i = 0}^k f_i \binom{m + n - a_i - 1}{m - n - a_i} \end{aligned}\]

由于本题的模数较小,我们可以通过 Lucas 定理在 \(O(\sqrt n\log n + \text{mod})\) 的复杂度内计算上式的值。

Submission.

posted @ 2023-03-01 17:10  joke3579  阅读(119)  评论(2编辑  收藏  举报