1. 程式人生 > >[期望DP]「BJOI2018」治療之雨

[期望DP]「BJOI2018」治療之雨

不能 using mes fine -i isp return stdin can

解題思路

\(g[i]\)表示\(k\)次攻擊後掉\(i\)滴血的概率,顯然有\(g[i]=C_{k}^{i}(\frac{1}{m+1})^i(\frac{m}{m+1})^{k-i}\)

\(f[i]\)表示英雄初始血量為\(i\)的期望存活輪數,那麽顯然有:
\[ f[i]=\sum_{j=0}^{min(i+1,k)}(\frac{1}{m+1})g[j]f[i+1-j]+\sum_{j=0}^{j=min(i,k)}(\frac{m}{m+1})g[j]f[i-j]+1 \]
血量為\(n\)時不能加血,所以轉移有所不同:
\[ f[n]=\sum_{j=0}^{j=min(n,k)}g[j]f[n-j]+1 \]


通過上面的第一個式子可以將\(f[i]\)推到\(f[i+1]\),然後發現一個很大的問題\(f[1]\)並不確定

所以我們將\(f[1]\)設為\(X\),那麽\(f\)的每個狀態都類似\(aX+b\)的形式,然後跟第二個式子解出\(X\)的值即可.

#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1505,tt=1e9+7;
struct jz{
    int x,y;
    jz(int x=0,int y=0):x(x),y(y){}
    jz operator+(const jz &b)const{return jz((x+b.x)%tt,(y+b.y)%tt);}
    jz operator-(const jz &b)const{return jz((x-b.x+tt)%tt,(y-b.y+tt)%tt);}
    jz operator*(int w)const{return jz((LL)x*w%tt,(LL)y*w%tt);}
}f[maxn],ans;
int g[maxn],n,p,m,k,T;
int qsm(int w,int b){int num=1;while(b){if (b&1) num=(LL)num*w%tt;w=(LL)w*w%tt;b>>=1;}return num;}
int work1(){
    if(k==1&&n!=1) return -1;
    g[0]=0;for(int i=1;i<n;i++) g[i]=g[max(i+1-k,0)]+1;
    g[n]=g[max(n-k,0)]+1;
    return g[p];
}
int work(){
    scanf("%d%d%d%d",&n,&p,&m,&k);int Invm=qsm(m+1,tt-2);
    if (!k) return -1;if (!m) return work1();
    int C=1;for (int i=0;i<=min(k,n);i++) g[i]=(LL)C*qsm(Invm,i)%tt*qsm((LL)m*Invm%tt,k-i)%tt,C=(LL)C*(k-i)%tt*qsm(i+1,tt-2)%tt;
    if (g[0]==1||g[0]==0) return -1;
    f[0]=jz(0,0);f[1]=jz(1,0);
    for (int i=1;i<n;i++){
        f[i+1]=(f[i]-jz(0,1))*(m+1);
        for (int j=0;j<=min(i,k);j++) f[i+1]=f[i+1]-(f[i-j]*g[j])*m;
        for (int j=1;j<=min(i+1,k);j++) f[i+1]=f[i+1]-(f[i+1-j]*g[j]);
        f[i+1]=f[i+1]*qsm(g[0],tt-2); 
    }
    ans=jz(0,1);for (int i=1;i<=min(n,k);i++) ans=ans+f[n-i]*g[i];
    ans=ans*qsm(1-g[0]+tt,tt-2);ans=ans-f[n];
    if (!ans.x&&f[p].x) return -1;int X=(LL)ans.y*qsm(tt-ans.x,tt-2)%tt;
    return ((LL)f[p].x*X+f[p].y)%tt;
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    scanf("%d",&T);while(T--) printf("%d\n",work());return 0;
} 

[期望DP]「BJOI2018」治療之雨