CCPC-Wannafly Comet OJ 夏季欢乐赛(2019)E.飞行棋(期望dp+矩阵快速幂)
生活随笔
收集整理的這篇文章主要介紹了
CCPC-Wannafly Comet OJ 夏季欢乐赛(2019)E.飞行棋(期望dp+矩阵快速幂)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目
飛行棋的規則如下:
1、每名玩家有一個棋子,每個回合可以擲一次骰子。
2、如果使用的骰子為?k面,則這?k面上的點數分別為 1,2,3,…,k,且擲得每種點數的概率均為?。
3、如果當前回合擲得的點數為?Q,則玩家控制的棋子前進?Q?步。
4、若當前棋子的位置到終點的距離?d < Q,則棋子先行動?d?步到終點,再倒退?Q-d?步。(即到終點的距離變為?Q?d)
5、某一回合結束后,若該玩家的棋子恰好到達終點,則宣布勝利。
璇璇姐姐參與了這個游戲,已知現在她的棋子到終點的距離為?d,
游戲使用的骰子有?k面,求璇璇獲得勝利需要的回合數期望。
保證?1<=k<=20,1<=d<=1e18,且k<=d。
思路來源
fls
題解
考慮距離終點k步及以內的情形,dp[i]代表從i這個點,走到終點的期望數
那么dp[1],擲到一步獲勝,擲到兩步回到1的位置,擲到三步到2的位置,擲到k步去k-1的位置
有,其中dp[0]=0
類似地有,
可以列出所有方程,
可以發現,由于右邊每項括號里都有k-1項非零值,高度對稱,
所以可以構造一組全相等的解,代入x有x=k
后面的仍用搞矩陣快速冪即可
?
學到了把k+1階向量也搞成(k+1)*1的矩陣技巧,
這樣最后求第n項時,再搞一個矩陣乘法即可
代碼
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long ll; const ll mod = 1e9+7; const ll MOD = 1e9+7; const int MAXN = 22; ll d,k,inv,ans; ll modpow(ll x,ll n,ll mod) {ll res=1;for(;n;n/=2,x=x*x%mod)if(n&1)res=res*x%mod;return res; } struct mat {ll c[MAXN][MAXN];int m, n;mat(){memset(c, 0, sizeof(c));m=n=MAXN;} mat(int a, int b) : m(a), n(b) {memset(c, 0, sizeof(c));}void clear(){memset(c, 0, sizeof(c)); }mat operator * (const mat& temp) {mat ans(m, temp.n);for (int i = 0; i < m; i ++)for (int j = 0; j < temp.n; j ++){for (int k = 0; k < n; k ++){ans.c[i][j] += c[i][k] * temp.c[k][j]%MOD;//能不取模 盡量不取模//這里maxn=2 故不會超過ll 視具體情況 改變取模情況if(ans.c[i][j]>=MOD)ans.c[i][j]%=MOD;}}return ans;}friend mat operator ^(mat M, ll n) {mat ans(M.m, M.m);for (int i = 0; i < M.m; i ++)ans.c[i][i] = 1; while (n > 0) {if (n & 1) ans = ans * M;M = M * M;n >>= 1;}return ans;} }; int main() {scanf("%lld%lld",&d,&k);mat a(k+1,k+1),b(k+1,1); inv=modpow(k,mod-2,mod);for(int i=0;i<k;++i)a.c[0][i]=inv;a.c[0][k]=1;for(int i=1;i<k;++i)a.c[i][i-1]=1;a.c[k][k]=1;a=a^(d-k);for(int i=0;i<k;++i)b.c[i][0]=k;b.c[k][0]=1;a=a*b;printf("%lld\n",a.c[0][0]);return 0; }?
總結
以上是生活随笔為你收集整理的CCPC-Wannafly Comet OJ 夏季欢乐赛(2019)E.飞行棋(期望dp+矩阵快速幂)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CCPC-Wannafly Comet
- 下一篇: 每日一题 | 计算松鼠的数量