1. 程式人生 > 實用技巧 >LOJ3160 「NOI2019」鬥主地

LOJ3160 「NOI2019」鬥主地

LOJ3160 「NOI2019」鬥主地

正解

想了半天發現自己連第一步都沒有想出來……翻翻題解發現人們都不屑於講這步……

就是說,洗牌之後,每種可能出現的方案的出現概率是相同的。

為什麼……

從初始狀態開始,一個最終狀態的概率是若干步的概率乘起來的。看分母,是\(n!\);看分子,是\(A_i!(n-A_i)!\),從而得證,概率為\(\frac{1}{\binom{n}{A_i}}\)

當然有個組合意義的說法:因為兩堆牌的內部的順序固定,所以它們之間不存在區別。那麼相當於有\(n\)個球,\(A_i\)個黑球\(n-A_i\)個白球,每次隨機取出一個。最終形成的排列和原來的模型是一一對應的。

接下來是個結論:\(E_i(x)\)表示\(i\)次操作之後\(x\)位置上值的期望,它可以表示成一個關於\(x\)的二次多項式的形式。

知道這個結論之後,從\(E_{i-1}(x)\)推到\(E_i(x)\),只需要隨便取三個值帶入\(E_i(x)\)算,算完之後插值就可以得到它的多項式係數了。

做法講完了,接下來是證明:

考慮歸納:

列舉位置\(x\)上的球轉移到位置\(y\)的概率,於是有:\(\binom{n}{A_i} E_i(y)=\sum_{x=1}^{A_i}\binom{y-1}{x-1}\binom{n-y}{A_i-x}E_{i-1}(x)+\sum_{x=1}^{n-A_i}\binom{y-1}{x-1}\binom{n-y}{n-A_i-x}E_{i-1}(A_i+x)\)

我們要證這條式子是個只和\(y\)有關的多項式。下面只考慮求左邊,另一邊同理:

由於\(E_{i-1}(x)\)為多項式,所以可以把它拆開成若干項分別算。直接普通的多項式可能不太好算,於是這裡取\(E_{i-1}(x)=\sum_{k=0}^2f_k(x-1)^{\underline k}\)

於是要證:\(\sum_{x=1}^{A_i}\binom{y-1}{x-1}\binom{n-y}{A_i-x}(x-1)^{\underline k}\)是個和\(y\)有關的多項式。

\[原式=\binom{y-1}{k}k!\sum_{x=1}^{A_i}\binom{y-1-k}{x-1-k}\binom{n-y}{A_i-x}=\binom{y-1}{k}k!\binom{n-k-1}{A_i-k-1} \]

(此處省略若干步驟,想推的同學慢慢推……)

看到這條式子和\(x\)無關,並且顯然有關\(y\)的二次多項式。由此得證。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 10000010
#define M 500010
#define ll long long
#define mo 998244353
ll qpow(ll x, ll y = mo - 2) {
    ll r = 1;
    for (; y; y >>= 1, x = x * x % mo)
        if (y & 1)
            r = r * x % mo;
    return r;
}
int n, m, type;
int fac[N], ifac[N];
void initC(int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; ++i) fac[i] = (ll)fac[i - 1] * i % mo;
    ifac[n] = qpow(fac[n]);
    for (int i = n - 1; i >= 0; --i) ifac[i] = (ll)ifac[i + 1] * (i + 1) % mo;
}
ll C(int m, int n) { return (ll)fac[m] * ifac[n] % mo * ifac[m - n] % mo; }
ll f0, f1, f2;
ll dot(ll x) { return ((f2 * x + f1) % mo * x + f0) % mo; }
void cha_1n2(ll y1, ll yn, ll y2) {
    static ll z1 = qpow(n - 1), zn = qpow((ll)(n - 1) * (n - 2) % mo), z2 = mo - qpow(n - 2);
    y1 = y1 * z1 % mo;
    yn = yn * zn % mo;
    y2 = y2 * z2 % mo;
    f0 = (y1 * n * 2 + yn * 2 + y2 * n) % mo;
    f1 = (mo - (y1 * (n + 2) + yn * 3 + y2 * (n + 1)) % mo) % mo;
    f2 = (y1 + yn + y2) % mo;
}
int main() {
    // freopen("in.txt","r",stdin);
    freopen("landlords.in", "r", stdin);
    freopen("landlords.out", "w", stdout);
    scanf("%d%d%d", &n, &m, &type);
    initC(n);
    f0 = 0, f1 = (type == 1), f2 = (type == 2);
    for (int i = 1; i <= m; ++i) {
        int A;
        ll E1, En, E2;
        scanf("%d", &A);
        E1 = ((1 <= A ? C(n - 1, A - 1) * dot(1) : 0) + (1 <= n - A ? C(n - 1, n - A - 1) * dot(1 + A) : 0)) %
             mo;
        En = ((1 <= A ? C(n - 1, A - 1) * dot(A) : 0) + (1 <= n - A ? C(n - 1, n - A - 1) * dot(n) : 0)) % mo;
        E2 = ((1 <= A ? C(n - 2, A - 1) * dot(1) : 0) + (1 <= n - A ? C(n - 2, n - A - 1) * dot(1 + A) : 0) +
              (2 <= A ? C(n - 2, A - 2) * dot(2) : 0) + (2 <= n - A ? C(n - 2, n - A - 2) * dot(2 + A) : 0)) %
             mo;
        ll invC = (ll)ifac[n] * fac[n - A] % mo * fac[A] % mo;
        cha_1n2(E1 * invC % mo, En * invC % mo, E2 * invC % mo);
    }
    int Q;
    scanf("%d", &Q);
    while (Q--) {
        int x;
        scanf("%d", &x);
        printf("%lld\n", dot(x));
    }
    return 0;
}