• loj2513 治疗之雨


    题意:你的英雄一开始血量为p,你还有m个队友,血量无穷。血量上限为n,下限为0。如果血量满了就不能加血。每次启动操作,随机给m+1个英雄加1点血,然后等概率随机k次每次对于英雄扣1点血。求期望操作几次你的英雄没血?

    n,m,p<=1500.

    标程:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int mod=1e9+7; 
     5 const int N=1505;
     6 int n,p,m,k,f[N],inv[N],g[N][N],ans[N];
     7 int ksm(int x,int y)
     8 {
     9     int res=1;
    10     while (y) {if (y&1) res=(ll)res*x%mod; y>>=1;x=(ll)x*x%mod;}
    11     return res;
    12 }
    13 int Inv(int x){return ksm(x,mod-2);}
    14 void gauss()
    15 {
    16     for (int i=n;i>=2;i--)
    17     {
    18         if (g[i][i]==0&&g[i-1][i]==0) {puts("-1");return;}
    19         if (g[i][i]==0) 
    20         {
    21           for (int j=1;j<=i;j++) swap(g[i][j],g[i-1][j]);
    22           swap(g[i][n+1],g[i-1][n+1]);
    23         }
    24         else
    25         {
    26           if (!g[i-1][i]) continue;
    27           int v=(ll)g[i-1][i]*Inv(g[i][i])%mod;
    28           for (int j=1;j<=i;j++)
    29             g[i-1][j]=((ll)g[i-1][j]-(ll)g[i][j]*v%mod+mod)%mod;
    30           g[i-1][n+1]=((ll)g[i-1][n+1]-(ll)g[i][n+1]*v%mod+mod)%mod;
    31         }
    32     }
    33     for (int i=1;i<=p;i++)
    34     {
    35         ans[i]=g[i][n+1];
    36         for (int j=1;j<i;j++)
    37           ans[i]=((ll)ans[i]-(ll)ans[j]*g[i][j]%mod+mod)%mod;
    38         ans[i]=(ll)ans[i]*Inv(g[i][i])%mod;
    39         if (!ans[i]) {puts("-1");return;}
    40     }
    41     printf("%d
    ",ans[p]);
    42 }
    43 void init()
    44 {
    45     memset(g,0,sizeof(g));
    46     int tmp=1,c=1,in,inn;in=inn=Inv(m+1);
    47     for (int i=1;i<=min(n,k);i++)
    48     {
    49         c=(ll)c*(k-i+1)%mod*inv[i]%mod;
    50         tmp=(ll)tmp*in%mod;
    51         f[i]=(ll)c*tmp%mod;
    52     }
    53     in=(ll)m*in%mod;tmp=ksm(in,k-min(n,k));
    54     f[0]=1;
    55     for (int i=min(n,k);i>=0;i--)
    56       f[i]=(ll)f[i]*tmp%mod,tmp=(ll)tmp*in%mod;
    57     
    58     for (int i=1;i<=n;i++)
    59     {
    60         if (i==n) in=1,inn=0;
    61         for (int j=max(i-k,0);j<=i;j++) 
    62         {
    63           g[i][j]=((ll)g[i][j]+(ll)f[i-j]*in%mod)%mod;
    64           if (j+1<=n) g[i][j+1]=(ll)f[i-j]*inn%mod;
    65         }
    66         g[i][i]=((ll)g[i][i]-1+mod)%mod;g[i][n+1]=mod-1;
    67     }
    68 }
    69 int main()
    70 {
    71     int T;scanf("%d",&T);
    72     inv[0]=inv[1]=1; for (int i=2;i<=1500;i++) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
    73     while (T--) {
    74         scanf("%d%d%d%d",&n,&p,&m,&k);
    75         init();gauss();
    76     }
    77     return 0;
    78 }

    易错点:1.注意对无解的特判,如果有概率为0就是-1。

    题解:dp+高斯消元

    一般dp式子:dp[i]表示还剩下i滴血直到英雄死去的期望操作次数。分自己的英雄是否加血讨论。f[i]表示血量减少i的概率,可以预处理。

    $dp[i]=(sum_{j=i-k}^{i}dp[j]*f[i-j]*m/(m+1)+sum_{j=i-k+1}^{i+1}dp[j]*f[i+1-j]*1/(m+1))+1$。

    但是由于有dp[i+1],难以递推。考虑设未知数进行高斯消元。高斯消元不是n^3*logn的吗?

    由于这个矩阵写出来是阶梯状往右,第i行的最右边元素只到i+1,所以只用下面一行来减去它,化成右下三角矩阵求即可。时间复杂度O(n^2(*logn))。

    或者直接用前n-1个式子写出dp[i]=Ax+B的表达式,然后再全部代入最后一个dp[n]=....的式子中,解Ax+B=A'x+B'即可。

  • 相关阅读:
    Singing Everywhere ZOJ
    CONTINUE...? ZOJ
    Doki Doki Literature Club ZOJ
    数论证明链接整理
    ACM数学(转)
    codeforce 1152C. Neko does Maths(欧几里得算法)
    八数码问题判定是否解的证明
    sql给表取别名
    win10激活方法
    HDU1121 Complete the Sequence(差分找规律)
  • 原文地址:https://www.cnblogs.com/Scx117/p/8921155.html
Copyright © 2020-2023  润新知