[BJOI2018]治療之雨
阿新 • • 發佈:2019-02-23
復雜 true string %d mes continue lock space n)
當然\(f_n\)的情況比較特殊,因為已經有\(n\)滴血的時候不會加血
這時候你可能會發現一個問題
\(f_0\)這一項去哪裏了?
這一項的系數其實並不好計算,因為可能這一輪還沒有結束血就已經扣沒了
但是\(f_0=0\),所以如果要從\(f_0\)轉移過來的話\(f_0\)前面的系數就恰好跟著\(f_0\)一起被消掉了
而且\(f_0\)不能從其他後繼狀態轉移,因為\(f_0\)就是一個結束狀態
這也就是期望倒著推的優點所在
還沒完==
然後發現\(n=1500\)
那麽不能暴力消元了
但是觀察矩陣可以發現第\(i\)行一定有且只有\(i+1\)項
所以可以從第i行一行一行的消下來的時候只消第i,i+1,n+1三項
復雜度\(O(Tn^2)\)
題解
高斯消元+期望
首先好像有那麽一句話叫概率正著推期望倒著推==
讓你計算期望
那麽考慮倒著推
設\(f_S\)表示剩余血量為\(S\)時距離結束的期望步數
然後顯然\(f_0=0\)
考慮每一輪,先有\(1\)次回血,再有\(k\)段攻擊
那麽可以計算出每次扣\(t\)滴血的期望是\(atk[t]=C(_{t}^{k})(\frac{1}{m+1})^t(\frac{m}{m+1})^{k-t}\)
然後先預處理出來\(atk[]\)
轉移是成環的,只能高斯消元
因為是倒著推得
所以顯然枚舉所有的後繼即可:\(f_i=1+\sum_{j=1}^{i}{f[j]*atk[i-j]*\frac{m}{m+1} + f[j]*atk[i-j+1]*\frac{1}{m+1}}\)
當然\(f_n\)的情況比較特殊,因為已經有\(n\)滴血的時候不會加血
這時候你可能會發現一個問題
\(f_0\)這一項去哪裏了?
這一項的系數其實並不好計算,因為可能這一輪還沒有結束血就已經扣沒了
但是\(f_0=0\),所以如果要從\(f_0\)轉移過來的話\(f_0\)前面的系數就恰好跟著\(f_0\)一起被消掉了
而且\(f_0\)不能從其他後繼狀態轉移,因為\(f_0\)就是一個結束狀態
這也就是期望倒著推的優點所在
還沒完==
然後發現\(n=1500\)
那麽不能暴力消元了
但是觀察矩陣可以發現第\(i\)行一定有且只有\(i+1\)項
所以可以從第i行一行一行的消下來的時候只消第i,i+1,n+1三項
代碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int M = 1505 ; const int mod = 1e9 + 7 ; using namespace std ; inline int read() { char c = getchar() ; int x = 0 , w = 1 ; while(c>'9' || c<'0') { if(c=='-') w = -1 ; c = getchar() ; } while(c>='0' && c<='9') { x = x*10+c-'0' ; c = getchar() ; } return x*w ; } int n , stp , m , k ; int fac[M] , hit[M] , miss[M] , atk[M] ; int B[M][M] , wei[M] ; inline int Fpw(int Base , int k) { int temp = 1 ; while(k) { if(k & 1) temp = (1LL * temp * Base) % mod ; Base = (1LL * Base * Base) % mod ; k >>= 1 ; } return temp ; } inline int Inv(int x) { return Fpw(x , mod - 2) ; } inline int Gfac(int l , int r) { int temp = 1 ; for(int i = l ; i <= r ; i ++) temp = 1LL * temp * i % mod ; return temp ; } inline void Pre_Solve() { for(int i = 1 ; i <= n ; i ++) for(int j = 1 ; j <= n + 1 ; j ++) B[i][j] = 0 ; fac[0] = 1 ; for(int i = 1 ; i <= n ; i ++) fac[i] = 1LL * fac[i - 1] * i % mod ; hit[0] = miss[0] = 1 ; hit[1] = Inv(m + 1) ; miss[1] = 1LL * m * Inv(m + 1) % mod ; for(int i = 0 ; i <= min(n , k) ; i ++) atk[i] = 1LL * Gfac(k - i + 1 , k) * Inv(fac[i]) % mod * Fpw(hit[1] , i) % mod * Fpw(miss[1] , k - i) % mod ; } inline bool gauss() { for(int i = 1 ; i <= n ; i ++) { wei[i] = min(i + 1 , n) ; for(int j = 1 ; j <= n + 1 ; j ++) B[i][j] = (B[i][j] % mod + mod) % mod ; } for(int i = 1 ; i <= n ; i ++) { if(B[i][i] == 0) return false ; int tinv = Inv(B[i][i]) ; for(int j = i + 1 ; j <= n ; j ++) { if(B[j][i] == 0) continue ; int tp = 1LL * B[j][i] * tinv % mod ; B[j][i] = ((B[j][i] - 1LL * tp * B[i][i] % mod) % mod + mod) % mod ; if(i + 1 <= n) B[j][i + 1] = ((B[j][i + 1] - 1LL * tp * B[i][i + 1] % mod) % mod + mod) % mod ; B[j][n + 1] = ((B[j][n + 1] - 1LL * tp * B[i][n + 1] % mod) % mod + mod) % mod ; } } for(int i = n ; i >= 1 ; i --) { for(int j = i + 1 ; j <= n ; j ++) B[i][n + 1] = ((B[i][n + 1] - 1LL * B[i][j] * B[j][n + 1] % mod) % mod + mod) % mod ; B[i][n + 1] = (1LL * B[i][n + 1] * Inv(B[i][i]) % mod + mod) % mod ; if(i == stp) break ; } return true ; } int main() { int T = read() ; while(T --) { n = read() ; stp = read() ; m = read() ; k = read() ; if(k == 0) { printf("-1\n") ; continue ; } else if(k == 1 && m == 0) { printf("-1\n") ; continue ; } Pre_Solve() ; for(int i = 1 ; i <= n ; i ++) { B[i][i] = (B[i][i] + 1) % mod ; B[i][n + 1] = (B[i][n + 1] + 1) % mod ; if(i < n) for(int j = 0 ; j <= i + 1 ; j ++) { if(i - j <= k) B[i][j] = ((B[i][j] - 1LL * atk[i - j] * miss[1] % mod) % mod + mod) % mod ; if(i - j + 1 <= k) B[i][j] = ((B[i][j] - 1LL * atk[i - j + 1] * hit[1] % mod) % mod + mod) % mod ; } else for(int j = 0 ; j <= i ; j ++) { if(i - j <= k) B[i][j] = ((B[i][j] - 1LL * atk[i - j] % mod) % mod + mod) % mod ; } } if(!gauss()) printf("-1\n") ; else printf("%d\n",(B[stp][n + 1] % mod + mod) % mod) ; } return 0 ; }
[BJOI2018]治療之雨