「清華集訓 2017」小 Y 和恐怖的奴隸主
為什麼這裡的題解都是寫的順推呢?這裡提供一篇倒推的題解,為那些和我一樣打倒退寫wa的小夥伴提供一點能夠借鑑的程式碼。
對於這道弱化版的題目,我們考慮倒推,定義\(f_{i,j,k,w}\)我們在被打了\(i-1\)次後,還剩下\(j\)個三血奴隸主,\(k\)個兩血奴隸主,\(w\)個一血奴隸主在接下來的\(i\)~\(k\)次被打中受到傷害的期望。
那麼轉移就會比較容易:
定義\(tot = j+k+w+1\)
當\(j+k+w < 7\)時有:
\[f_{i,j,k,w} = (f_{i+1,j,k,w}+1) \times \frac{1}{tot}+ f_{i+1,j,k+1,w} \times \frac {j}{tot} + f_{i+1,j+1,k-1,w+1} \times \frac{k}{tot} +f_{i+1,j,k,w-1} \times \frac{w}{tot} \]
否則有:
\[f_{i,j,k,w} = f_{i+1,j-1,k+1,w} \times \frac {j}{tot} + f_{i+1,j,k-1,w+1} \times \frac{k}{tot} +f_{i+1,j,k,w-1} \times \frac{w}{tot} \]
程式碼比較簡單,注意一下輸入順序就好了。
現在我們來看這一道題,我們仍然考慮倒退,發現\(n \leq 10^{18}\),我們需要優化,我們發現對於每個\(dp\)狀態的轉移的引數與\(i\)並無關係,因此我們考慮矩陣加速。
我們枚舉出\(j,k,w\)的所有合法情況(由於我們轉移的特殊性,我們需要再把1作為常數項加入矩陣參與轉移),將其作為矩陣的大小,由於\(K \leq 8\)
但是這個複雜度我們仍然無法通過本題,我們考慮繼續優化。我們發現這個矩陣是固定的,因此我們可以預處理出矩陣的\(2^i\)的結果,我們在處理詢問的時候只需要用一個單行去乘以我們預處理出來的一些矩陣就可以了。
但是它還是死了,根據一些奇技淫巧,我們可以用__in128儲存矩陣乘法的中間結果,最後再去取模,就能有效地減少取模次數,優化效果顯著。
下面就是十分冗長的程式碼:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; const LL mod = 998244353; struct matrix { LL a[170][170]; int n , m; matrix operator * (const matrix &b) const{ matrix c; __int128 C[170][170]; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= b.m; ++j) { C[i][j] = 0; } } for (int i = 1; i <= n; ++i) { for (int k = 1; k <= m; ++k) { for (int j = 1; j <= b.m; ++j) { C[i][j] = C[i][j] + a[i][k] * b.a[k][j]; } } } for (int i = 1; i <= n; ++i) { for (int j = 1; j <= b.m; ++j) { c.a[i][j] = C[i][j] % mod; } } c.n = n; c.m = b.m; return c; } }I; int T , m , K , tot , id[10][10][10]; matrix A , B[64] , beg , ans; LL get_inv(LL a) { int b = mod - 2; LL res = 1; while(b) { if(b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } void get_matrix_3 () { for (int j = 0; j <= K; ++j) { for (int k = 0; k <= K; ++k) { for (int w = 0; w <= K; ++w) { if(j + k + w > K) continue; id[j][k][w] = ++tot; } } } ++tot; A.n = A.m = tot; A.a[tot][tot] = 1; for (int j = 0; j <= K; ++j) { for (int k = 0; k <= K; ++k) { for (int w = 0; w <= K; ++w) { if(j + k + w > K) continue; int now = id[j][k][w]; LL tmp = get_inv((LL)(j + k + w + 1)); A.a[now][now] = tmp;A.a[now][tot] = tmp; if(j) { if(j + k + w < K) A.a[now][id[j][k+1][w]] = (LL)j * tmp % mod; else A.a[now][id[j-1][k+1][w]] = (LL)j * tmp % mod; } if(k) { if(j + k + w < K) A.a[now][id[j+1][k-1][w+1]] = (LL)k * tmp % mod; else A.a[now][id[j][k-1][w+1]] = (LL)k * tmp % mod; } if(w) A.a[now][id[j][k][w-1]] = (LL)w * tmp % mod; } } } } int id2[10][10]; void get_matrix_2 () { for (int j = 0; j <= K; ++j) { for (int k = 0; k <= K; ++k) { if(j + k > K) continue; id2[j][k] = ++tot; } } ++tot; I.n = I.m = A.n = A.m = tot; for (int j = 0; j <= K; ++j) { for (int k = 0; k <= K; ++k) { if(j + k > K) continue; int now = id2[j][k]; LL tmp = get_inv((LL)(j + k + 1)); A.a[now][now] = tmp;A.a[now][tot] = tmp; if(j) { if(j + k < K) A.a[now][id2[j][k+1]] = (LL)j * tmp % mod; else A.a[now][id2[j-1][k+1]] = (LL)j * tmp % mod; } if(k) A.a[now][id2[j][k-1]] = (LL)k * tmp % mod; } } A.a[tot][tot] = 1; } void make_pow() { B[0] = A; for (int i = 1; i <= 59; ++i) B[i] = B[i - 1] * B[i - 1]; } LL f[15][10]; void work_1(int n) { for (int i = n; i >= 1; --i) { for (int j = 0; j <= K; ++j) { LL tmp = get_inv((LL)(j + 1)); f[i][j] = (f[i+1][j] + 1) * tmp % mod; if(j) f[i][j] += f[i+1][j-1] * (LL)j % mod * tmp % mod; } } printf("%lld\n" , f[1][1]); memset(f , 0 , sizeof f); } int main() { // freopen("10.in" , "r" , stdin); scanf("%d %d %d" , &T , &m , &K); if(m == 3) { get_matrix_3(); make_pow(); ans.n = tot;ans.m = 1; while(T -- > 0) { LL x; scanf("%lld" , &x); for (int i = 1; i < tot; ++i) ans.a[i][1] = 0; ans.a[tot][1] = 1; for (LL i = 60; i >= 1; --i) { if(x >= (1ll << (i - 1ll))) { x -= (1ll << (i - 1ll)); ans = B[i - 1] * ans; } } printf("%lld\n" , ans.a[id[1][0][0]][1]); } } else if(m == 2) { get_matrix_2(); make_pow(); beg.n = tot;beg.m = 1; beg.a[tot][1] = 1; while(T -- > 0) { LL x; scanf("%lld" , &x); ans = beg; for (LL i = 60; i >= 1; --i) { if(x >= (1ll << (i - 1ll))) { x -= (1ll << (i - 1ll)); ans = B[i - 1] * ans; } } printf("%lld\n" , ans.a[id2[1][0]][1]); } } else { while(T -- > 0) { int n; scanf("%d" , &n); work_1(n); } } return 0; }