Bootstrap

洛谷P4457 [BJOI2018]治疗之雨(期望+高斯消元)

题目链接
推下式子
假设就先不管加血
m m m个人随机搞 k k k次,掉 j j j第血的概率为 p [ j ] p[j] p[j]
显然就是从 k k k次里选 j j j次刚他,得到公式
p [ j ] = C k j ( 1 m + 1 ) j ( m m + 1 ) k − j p[j]=C_k^j(\frac{1}{m+1})^j(\frac{m}{m+1})^{k-j} p[j]=Ckj(m+11)j(m+1m)kj
嘛,这个东西是可以递推出来的。
那么接着考虑奶的
显然把他从 i i i刚到 j j j要么掉了 i − j + 1 i-j+1 ij+1被奶了一口,要么掉了 i − j i-j ij脸比较黑没有奶
p p [ i ] [ j ] = 1 m + 1 p [ i − j + 1 ] + m m + 1 p [ i − j ] pp[i][j]=\frac{1}{m+1}p[i-j+1]+\frac{m}{m+1}p[i-j] pp[i][j]=m+11p[ij+1]+m+1mp[ij]
好的,然后开始考虑推期望,期望 E [ i ] E[i] E[i]表示 i i i滴血被刚到0的期望步数
边界为 E 0 = 0 E_0=0 E0=0,因为已经凉了
E i = ( ∑ j = 0 i E j p p [ i ] [ j ] ) + ( 1 m + 1 p [ 0 ] E [ i + 1 ] ) + 1 E_i=(\sum_{j=0}^iE_{j}pp[i][j])+(\frac{1}{m+1}p[0]E[i+1])+1 Ei=(j=0iEjpp[i][j])+(m+11p[0]E[i+1])+1
后面只有 1 m + 1 \frac{1}{m+1} m+11的原因是这个时候只能靠脸白被奶且无伤了
眉头一皱发现不大妙,他居然有i+1这个项
只好高斯消元了
消元的时候有点特殊技巧
可以发现这是一个梯形矩阵,一行比上一行多一个
那么消元可以行与行间消
复杂度O(n^2)

代码如下

#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;

long long fac[2000],inv[2000];
long long f[1510],pp[1510][1510];
int n,p,m,k;

long long kasumi(long long a,long long b)
{
    long long ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

void init()
{
    fac[0]=1;
    for(int i=1;i<=1700;i++)
    {
        fac[i]=fac[i-1]*i%mod;
    }
    inv[1700]=kasumi(fac[1700],mod-2);
    for(int i=1699;i>=0;i--)
    {
        inv[i]=inv[i+1]*(i+1)%mod;
    }
}

inline long long c(long long a,long long b)
{
    return fac[a]*inv[b]%mod*inv[a-b]%mod;
}

int ttt;

int main()
{
	// freopen("1.out","w",stdout);
    init();
    scanf("%d",&ttt);
    while(ttt--)
    {
        memset(f,0,sizeof(f));
        memset(pp,0,sizeof(pp));
        scanf("%d%d%d%d",&n,&p,&m,&k);
        if(k==0) {puts("-1");continue;}
        if(m==0) 
        {
            int ans=0;
            if(k==1) {puts("-1");continue;}
            if(p==n) {p-=k,ans++;}
            for(;p>0;p-=k-1) ans++;
            printf("%d\n",ans);
            continue;
        }
        long long inv1=kasumi(m+1,mod-2);
        long long tmp=1;
        long long now=m*inv1%mod;
        long long inv2=kasumi(m,mod-2);
        now=kasumi(now,k);
        f[0]=now;
        for(int i=1;i<=n;i++)
        {
        	f[i]=f[i-1]*inv2%mod*kasumi(i,mod-2)%mod*(k-i+1)%mod;
        }
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                pp[i][j]=(m*f[i-j]+f[i-j+1])%mod*inv1%mod;
            }
        }
        for(int i=1;i<n;i++) pp[i][i+1]=f[0]*inv1%mod;
        for(int i=1;i<n;i++) pp[i][i]=(pp[i][i]+mod-1)%mod;
        for(int i=1;i<=n;i++) pp[i][n+1]=mod-1;
        for(int i=1;i<=n;i++) pp[n][i]=f[n-i];
        pp[n][n]=(pp[n][n]+mod-1)%mod;
        for(int i=1;i<=n;i++)
        {
            inv1=kasumi(pp[i][i],mod-2);
            pp[i][i]=1;
            pp[i][n+1]=pp[i][n+1]*inv1%mod;
            if(i!=n)
            {
                pp[i][i+1]=pp[i][i+1]*inv1%mod;
            }
            for(int j=i+1;j<=n;j++)
            {
                long long tmp=pp[j][i];
                pp[j][i]=0;
                pp[j][i+1]=(pp[j][i+1]-tmp*pp[i][i+1]%mod+mod)%mod;
                pp[j][n+1]=(pp[j][n+1]-tmp*pp[i][n+1]%mod+mod)%mod;
            }
        }
        for(int i=n;i>1;i--)
        {
            pp[i-1][n+1]=(pp[i-1][n+1]-pp[i-1][i]*pp[i][n+1]%mod+mod)%mod;
            pp[i-1][i]=0;
        }
        printf("%lld\n",pp[p][n+1]);
    }
}
;