[CTSC2018] 假面 | 指望 DP

題目連接

LOJ 2552
Luogu P4564ios

考場上這道題我先是寫了個70分暴力,而後發現彷佛能夠NTT,然鵝問題是——我沒學過NTT,遂腦補之,腦補出來了,下午出成績一看,卡成暴力分(70)……同是\(O(Qk^2\log k)\),學姐的拉格朗日什麼玩意就能過TAT……學姐太強了……優化

遂不忿,今天上午重寫NTT,努力卡常,卡不進去……spa

那仍是寫正解吧。code

首先,發現血量上限不多,0操做時,暴力維護每一時刻每一個人是每種血量大小的機率便可。get

1操做怎麼辦呢?設\(alive_i\)\(i\)號人活着的機率,\(dead_i\)是他死了的機率,\(g_{i,j}\)是除\(i\)之外活了\(j\)我的的機率,\(i\)號人的答案就是\[alive_i * \sum_{j = 0}^{k - 1}\frac{1}{j + 1} * g_{i,j}\]string

可是\(g_{i,j}\)怎麼求呢?發現能夠DP:設\(f_{i,j}\)表示前\(i\)我的有\(j\)個活着的機率,則\[f_{ij} = f_{i-1,j} * dead_i + f_{i-1,j-1} * alive_i\]it

注意到最後的\(f\)和人的順序無關,因此能夠把人的順序任意調換,把要求的這個\(i\)放在最後一個,這樣\(f_{k - 1}\)就是\(g_{i}\)io

那麼對於每一個\(i\)求一遍\(f\),複雜度是\(O(n^3)\)的,能得70分。class

如何優化呢?考慮把\(i\)號人放在最後時,從\(f_k\)倒推到\(f_{k-1}\)\[\frac{f_{k-1, j} = f_{k, j} - f_{k -1, j - 1} * alive_i}{dead_i}\]stream

注意到\(dead_i = 0\)時該式不能用,又發現此時\(f_{k-1, j} = f_{k, j+1}\),因此也能直接求。

那麼\(O(n^2)\)求出\(f_k\),再\(O(n^2)\)倒推,直接能夠得到答案!

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#define space putchar(' ')
#define enter putchar('\n')
typedef long long ll;
using namespace std;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
    if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
    x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 256, P = 998244353;
int n, m, K, t[N], b[N], rate[N][105], iv[N];
ll qpow(ll a, ll x){
    ll ret = 1;
    while(x){
    if(x & 1) ret = ret * a % P;
    a = a * a % P;
    x >>= 1;
    }
    return ret;
}
void attack(int tar, ll x){
    ll y = (1 - x + P) % P;
    for(int i = 0; i <= b[tar]; i++){
    if(i) rate[tar][i] = rate[tar][i] * y % P;
    if(i < b[tar]) rate[tar][i] = (rate[tar][i] + rate[tar][i + 1] * x) % P;
    }
}
void query(){
    static ll f[N], g[N], h[N];
    memset(f, 0, sizeof(f));
    f[0] = 1;
    for(int i = 1; i <= K; i++)
    for(int j = i; j >= 0; j--)
        f[j] = ((j ? f[j - 1] * (1 - rate[t[i]][0]) : 0) + f[j] * rate[t[i]][0]) % P;
    for(int i = 1; i <= K; i++){
    h[i] = 0;
    if(!rate[t[i]][0])
        for(int j = 0; j < K; j++)
        h[i] += f[j + 1] * iv[j + 1] % P;
    else{
        int inv = qpow(rate[t[i]][0], P - 2);
        for(int j = 0; j < K; j++){
        g[j] = (f[j] - (j ? g[j - 1] * (1 - rate[t[i]][0]) : 0)) % P * inv % P;
        h[i] += iv[j + 1] * g[j] % P;
        }
    }
    h[i] %= P;
    h[i] = h[i] * (1 - rate[t[i]][0]) % P;
    if(h[i] < 0) h[i] += P;
    }
    for(int i = 1; i <= K; i++)
    write(h[i]), i == K ? enter: space; 
}

int main(){

    read(n);
    for(int i = 1; i <= n; i++)
    read(b[i]), rate[i][b[i]] = 1, iv[i] = qpow(i, P - 2);
    read(m);
    int op, x, u, v;
    while(m--){
    read(op);
    if(op == 0) read(x), read(u), read(v), attack(x, u * qpow(v, P - 2) % P);
    else{
        read(K);
        for(int i = 1; i <= K; i++) read(t[i]);
        query();
    }
    }
    for(int i = 1; i <= n; i++){
    ll sum = 0;
    for(int j = 1; j <= b[i]; j++)
        sum += (ll)j * rate[i][j] % P;
    write(sum % P), i == n ? enter: space;
    }
    
    return 0;
}
相關文章
相關標籤/搜索