• BZOJ5292 & 洛谷4457 & LOJ2513:[BJOI2018]治疗之雨——题解


    https://www.lydsy.com/JudgeOnline/problem.php?id=5292

    https://www.luogu.org/problemnew/show/P4457

    https://loj.ac/problem/2513

    你现在有m+1个数:第一个为p,最小值为0,最大值为n;剩下m个都是无穷,没有最小值或最大值。
    你可以进行任意多轮操作,每轮操作如下:
    在不为最大值的数中等概率随机选择一个(如果没有则不操作),把它加一;
    进行k次这个步骤:在不为最小值的数中等概率随机选择一个(如果没有则不操作),把它减一。
    现在问期望进行多少轮操作以后第一个数会变为最小值0。

    期望dp的一个惯用套路。

    设f[i]为正好打中英雄i下的概率,则f[i]=C(k,i)*(1/(m+1))^k*(m/(m+1))^(k-i)

    于是不难得到f[i]与f[i-1]之间的关系,则线性推之。

    再设a[i][j]为英雄i血经过一轮变j血的概率。

    则a[i][j]=(f[i-j]*m+f[i+1-j])/(m+1),但是当i=n时不适用,当i<j时也不适用,二者特判之。

    最后令x[i]为英雄到i血的期望。

    则有:

    x[0]=0;

    x[1]=a[1][2]*x[2]+a[1][1]*x[1]+1

    x[2]=a[2][3]*x[3]+a[2][2]*x[2]+a[2][1]*x[1]+1

    ……

    x[n]=a[n][n]*x[n]+a[n][n-1]*x[n-1]+……

    (注意最后一项有细微差别。)

    此时我们可以高斯消元求出答案……?

    n=1500仿佛在开玩笑。

    那么这样的式子一定有什么特殊的方法可以消元。

    我们每次用1式消掉x[1],用2式x[2]……,则复杂度其实只需要O(n^2)(因为1式只有2个数,2式消完后也只有2个数……以此类推)

    这样我们每一个式子就是一个二元一次方程,最后一个式子是一个一元一次方程,于是就可以求解了。

    (PS:还有其他比如k=0/1,m=0之类的特判不要忘了加。)

    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=1505;
    const int p=1e9+7;
    inline ll read(){
        ll X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    ll qpow(ll k,int n){
        ll res=1;
        while(n){
        if(n&1)res=res*k%p;
        k=k*k%p;n>>=1;
        }
        return res;
    }
    ll n,q,m,k,f[N],a[N][N],x[N];
    inline void init(){
        ll inv1=qpow(m+1,p-2),inv2=qpow(m,p-2);
        f[0]=qpow(inv1*m%p,k);
        for(int i=1;i<=min(n,k);i++)
        f[i]=f[i-1]*inv2%p*qpow(i,p-2)%p*(k-i+1)%p;
        
        for(int i=1;i<n;i++){
        for(int j=1;j<=i;j++)
            a[i][j]=(f[i-j]*m%p+f[i+1-j])%p*inv1%p;
        a[i][i+1]=f[0]*inv1%p;
        }
        for(int j=1;j<=n;j++)a[n][j]=f[n-j];
        for(int i=1;i<=n;i++)a[i][n+1]=p-1;
        for(int i=1;i<=n;i++)(a[i][i]+=p-1)%=p;
    }
    ll solve(){
        if(!k)return -1;
        if(!m){
        if(k==1)return -1;
        int res=0;
        while(q>0){if(q<n)q++;q-=k;res++;}
        return res;
        }
        init();
        for(int i=1;i<=n;i++){
        ll inv=qpow(a[i][i],p-2);
        a[i][i]=1;(a[i][n+1]*=inv)%=p;
        if(i!=n)(a[i][i+1]*=inv)%=p;
        for(int j=i+1;j<=n;j++){
            ll tmp=a[j][i];a[j][i]=0;
            (a[j][i+1]+=p-tmp*a[i][i+1]%p)%=p;
            (a[j][n+1]+=p-tmp*a[i][n+1]%p)%=p;
        }
        }
        x[n]=a[n][n+1];
        for(int i=n;i>q;i--)
        x[i-1]=(a[i-1][n+1]-a[i-1][i]*x[i]%p+p)%p;
        memset(f,0,sizeof(f));
        return x[q];
    }
    int main(){
        int t=read();
        while(t--){
        n=read(),q=read(),m=read(),k=read();
        printf("%lld
    ",solve());
        }
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

     +本文作者:luyouqi233。               +

     +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    SQLServer 使用ADSI执行分布式查询ActiveDorectory对象
    GridView的DataFormatString
    我的第一篇博客
    delphi for php 帮助文档的笔记(二)
    用delphiforphp来编写算法注册机第一节
    delphiforphp的中文环境的搭建
    初步拟定的delphiforphp的学习计划
    取當前日期各种數据庫的寫法(转存,备查)
    php两页间传变量(转发,备查)
    关于delphiforphp我想说的。
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9116458.html
Copyright © 2020-2023  润新知