loj#2325. 「清華集訓 2017」小 Y 和恐怖的奴隸主 (矩陣快速冪優化概率dp)

吐槽請無視
哇塞我終於開始更博客了!感不感動!興不興奮!%¥#%$#@*&....
emm事實上是因爲csdn的LaTeX終於修復好了。。

ps.

之後的題解可能都會相對簡略。
並且養成標題上加算法的好習慣,,

題面在這裏

題意:

維護一個集合,初始有兩個數 {+,m}
進行 n 次操作,每次隨機選一個數,把它減一;如果結果爲 0 ,把它刪掉;否則,如果集合大小不超過 k ,則添一個 m
最後問那個 + 期望被減了多少。
詢問 n 的次數 T1000 (極限數據爲 500 ),n1018,m3,k8

做法:

定義 fi,a,b,c 表示第 i 輪,血量爲1/2/3的分別剩下a/b/c 個奴隸主,此時的期望次數;將所有合法 a,b,c 的狀態找出來發現最多165種,於是重新定義, fi,S 表示第 i 輪,狀態爲 S 的期望次數,fi,S=1num+ssfi+1,S 。用矩陣快速冪優化這個dp。
然後每個詢問重新計算很慢,於是先 logn 預處理,複雜度 O(1653logn+T1652logn)
加一些卡常優化,,諸如開個大模數減少模的次數。

代碼:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 10;
const int M = 170;
const int mod = 998244353;
const ll lim = (0x7fffffffffffffffll/mod-mod)*mod;
int m, K, tot, id[N][N][N];
ll n;
ll inv[N], tmp[M], ans[M];
inline ll ksm(ll x, ll p) {
    ll ret = 1;
    for(; p; p >>= 1, x = x*x%mod) if(p&1) ret = ret*x%mod;
    return ret;
}
struct matrix {
    ll s[M][M];
    matrix() { memset(s, 0, sizeof s); }
} a[65];
inline matrix sqr(const matrix &x) {
    matrix ret;
    for(int i = 1; i <= tot+1; i ++)
        for(int j = 1; j <= tot+1; j ++) {
            for(int k = 1; k <= tot+1; k ++) {
                ret.s[i][j] += x.s[i][k]*x.s[k][j];
                if(ret.s[i][j] >= lim) ret.s[i][j] -= lim;
            }
            ret.s[i][j] %= mod;
        }
    return ret;
}
inline void mul(const matrix &x) {
    memset(tmp, 0, sizeof tmp);
    for(int i = 1; i <= tot+1; i ++) {
        for(int j = 1; j <= tot+1; j ++) {
            tmp[i] += ans[j]*x.s[j][i];
            if(tmp[i] >= lim) tmp[i] -= lim;
        }
        tmp[i] %= mod;
    }
    for(int i = 1; i <= tot+1; i ++) ans[i] = tmp[i];
}
int main() {
    int test;
    scanf("%d%d%d", &test, &m, &K);
    for(int i = 0; i <= K; i ++)
        for(int j = 0; j <= ((m>1)?K-i:0); j ++)
            for(int k = 0; k <= ((m>2)?K-i-j:0); k ++) id[i][j][k] = ++ tot;
    for(int i = 0; i <= K+1; i ++) inv[i] = ksm(i, mod-2);
    for(int i = 0; i <= K; i ++)
        for(int j = 0; j <= ((m>1)?K-i:0); j ++)
            for(int k = 0; k <= ((m>2)?K-i-j:0); k ++) {
                int now = id[i][j][k], nk = (i+j+k)<K; ll iv = inv[i+j+k+1];
                if(m >= 1) if(i) a[0].s[now][id[i-1][j][k]] = iv*i%mod;
                if(m >= 2) {
                    if(m == 2) if(j) a[0].s[now][id[i+1][j-1+nk][k]] = iv*j%mod;
                    if(m == 3) if(j) a[0].s[now][id[i+1][j-1][k+nk]] = iv*j%mod;
                }
                if(m >= 3) if(k) a[0].s[now][id[i][j+1][k-1+nk]] = iv*k%mod;
                a[0].s[now][now] = a[0].s[now][tot+1] = iv;
            }
    a[0].s[tot+1][tot+1] = 1;
    for(int i = 1; i <= 63; i ++) a[i] = sqr(a[i-1]);
    while(test --) {
        scanf("%lld", &n);
        memset(ans, 0, sizeof ans);
        if(m == 1) ans[id[1][0][0]] = 1;
        if(m == 2) ans[id[0][1][0]] = 1;
        if(m == 3) ans[id[0][0][1]] = 1;
        for(int i = 0; n; n >>= 1, i ++) if(n&1) mul(a[i]);
        printf("%lld\n", ans[tot+1]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章