LOJ 3160: 「NOI2019」斗主地

题目传送门:LOJ #3160

简要题意:

有一个长度为 \(n\) 的序列 \(a\),初始时 \(a_i=i\)\(a_i=i^2\),这取决于 \(\mathrm{type}\) 的值。

对这个序列进行 \(m\) 次操作,每次操作给定一个值 \(A_i\),把这个序列分为两部分:\(a[1:A_i]\)\(a[A_i+1:n]\),然后在不改变两个序列内部相对顺序的限制下,均匀地将这两个序列混合,形成新的序列,则新的序列 \(a\) 即为这个混合而成的新序列。

\(Q\) 次询问经过了这 \(m\) 次操作后,某个位置上的值 \(a_{c_i}\) 的期望。

题解:

因为是均匀的混合,所以在所有 \(\displaystyle\binom{n}{A_i}\) 种混合方式中,所有方式出现的概率均是相等的。

首先打一个 \(30\) 分的 \(\mathcal{O}(m\cdot n^2)\) 的暴力,或观察样例可以发现:做任意多次操作后,序列 \(\mathbb{E}[a_i]\) 仍然是一次函数或二次函数。

这个结论可以这样感性理解:

  • 首先,初始时是一次函数或者二次函数,只需证明一个一次函数或二次函数经过一次操作后次数仍然不变即可。
  • 考虑 \(\mathbb{E}[a_i]=f(i)\),其中 \(f(x)\) 是关于 \(x\) 的一个一次函数或二次函数。
  • 经过一次给定的值为 \(k\) 的操作后,左边的第 \(i\) 项等于 \(f(i)\),右边的第 \(i\) 项等于 \(f(k+i)\),左右两边也是次数相同的函数。
  • 两个一次函数或二次函数形成的序列均匀混合后对应的函数,次数是不应当增加的,而因为最高次项系数符号相同,最高次项也不会被抵消,所以次数也不会减少。

那么每次只需求出前三项的值即可,前三项只有可能由左边序列的前三项(这已经求出)和右边序列的前三项(需要插值求出)组合而成,只要推一些简单的式子就可以求出新的前三项的值了。

以下是代码,复杂度 \(\mathcal{O}(m)\)

#include <cstdio>

typedef long long LL;
const int Mod = 998244353;
const int Inv2 = (Mod + 1) / 2;

inline int qPow(int b, int e) {
    int a = 1;
    for (; e; e >>= 1, b = (LL)b * b % Mod)
        if (e & 1) a = (LL)a * b % Mod;
    return a;
}
inline int gInv(int x) { return qPow(x, Mod - 2); }

int N, M, A, Q, Typ;
LL iN0, iN1, iN2;
int E1, E2, E3;
inline int GetX(int i) {
    if (i == 1) return E1;
    if (i == 2) return E2;
    if (i == 3) return E3;
    int SE1 = (LL)E1 * (i - 2) % Mod * (i - 3) % Mod;
    int SE2 = (LL)E2 * (2 - i - i + Mod) % Mod * (i - 3) % Mod;
    int SE3 = (LL)E3 * (i - 1) % Mod * (i - 2) % Mod;
    return ((LL)SE1 + SE2 + SE3) * Inv2 % Mod;
}

int main() {
    freopen("landlords.in", "r", stdin);
    freopen("landlords.out", "w", stdout);
    scanf("%d%d%d", &N, &M, &Typ), --Typ;
    iN0 = gInv(N), iN1 = gInv((LL)N * (N - 1) % Mod), iN2 = gInv((LL)N * (N - 1) % Mod * (N - 2) % Mod);
    E1 = 1, E2 = Typ ? 4 : 2, E3 = Typ ? 9 : 3;
    while (M--) {
        scanf("%d", &A);
        LL F1 = E1, F2 = E2, F3 = E3;
        LL F4 = GetX(A + 1), F5 = GetX(A + 2), F6 = GetX(A + 3);
        E1 = (F1 * A + F4 * (N - A)) % Mod * iN0 % Mod;
        E2 = (F2 * A % Mod * (A - 1) + (F1 + F4) * A % Mod * (N - A) + F5 * (N - A) % Mod * (N - A - 1)) % Mod * iN1 % Mod;
        E3 = (F3 * A % Mod * (A - 1) % Mod * (A - 2) + (F4 + F2 + F2) * A % Mod * (A - 1) % Mod * (N - A) + (F5 + F5 + F1) * A % Mod * (N - A) % Mod * (N - A - 1) % Mod + F6 * (N - A) % Mod * (N - A - 1) % Mod * (N - A - 2)) % Mod * iN2 % Mod;
    }
    scanf("%d", &Q);
    for (int X; Q--; ) {
        scanf("%d", &X);
        printf("%d\n", GetX(X));
    }
    return 0;
}
posted @ 2019-07-21 12:39 粉兔 阅读(...) 评论(...) 编辑 收藏