zoukankan      html  css  js  c++  java
  • BJOI2018 治疗之雨

    在洛谷上发了份题解,链接:https://www.luogu.org/blog/user22151/solution-p4457

    我不是很会矩阵做这道题(不会卡常)。

    但是我们可以用本质差不多的方法,但是不用矩阵,可能常数会小一点?反正目前洛谷开O2不开O2、bz都是最快的……

    用$dp[i]$表示还剩$i$点血的时候,期望还要有多少轮操作。

    用$f[x]$表示$K$次中有$i$次打到英雄的概率。

    $f[x]=C(K,x)(frac{1}{m+1})^x(frac{m}{m+1})^{(k-x)}$

    首先对于$m=0$的情况特判。

    对于$m!=0$情况,我们发现有

    $dp[0]=0$

    $dp[1]=a_{1,2}dp[2]+a_{1,1}dp[1]+1$

    ...

    $dp[i]=a_{i,i+1}dp[i+1]+a_{i,i}dp[i]+...+a_{i,1}dp[1]+1$

    ...

    $dp[n]=a_{n,n}dp[n]+a_{n,n-1}dp[n-1]+...+a_{n,1}dp[1]+1$

    其中$a_{i,j}$表示英雄$i$点血在一轮操作后变成$j$点血的概率,这个可以根据$f$算出来,不再赘述。

    所以说我们可以得到$n$个等式,第$i$个式子是$dp[i]$等于多少多少

    $n$个未知数,$n$个等式(方程),很优秀的样子。

    但是我们发现,第$i$个等式右边有$dp[i+1]$,所以不能直接递推,感觉很恼火。

    我们把第$i$个式子右边的$dp[i+1]$放到等式左边来,然后把$dp[i]$放到等式右边,那么这个式子就变成了$dp[i+1]$等于多少多少。

    注意第$n$个式子例外,因为和$dp[n+1]$无关,仍是$dp[n]$等于多少多少。

    所以这样我们可以用第$i$个式子算$dp[i+1]$?

    但是我们算不出$dp[1]$呀,也没法直接往后递推。

    那我们先设$dp[1]=X$,那么我们可以根据第$1$~$n-1$个式子,把$dp[i]$表示成$AX+B$的形式,递推算出$dp[n]=A_1X+B_1$,然后根据第$n$个式子算出$dp[n]=A_2X+B_2$。

    那么$A_1X+B_1=A_2X+B_2$,解一元一次方程直接算出$X$。

    注意特判解不出$X$的情况输出-1。

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    const int maxn=3000+7;
    const ll mod=1e9+7;
    ll Td,n,P,m,K,X;
    
    char cc; ll ff;
    template<typename T>void read(T& aa) {
        aa=0;cc=getchar();ff=1;
        while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
    
    ll qp(ll x,ll k) {
        if(x<=1) return x;
        ll rs=1;
        while(k) {
            if(k&1) rs=rs*x%mod;
            k>>=1; x=x*x%mod;
        }
        return rs;
    }
    
    ll mo(ll x) {if(x>=mod) x-=mod; return x;}
    
    struct T{
        ll x,y;
        T(ll x=0.,ll y=0.):x(x),y(y){}
        inline T operator + (const T& b) const{return T(mo(x+b.x),mo(y+b.y));}
        inline T operator - (const T& b) const{return T(mo(x-b.x+mod),mo(y-b.y+mod));}
        inline T operator * (const T&b) const{return T((x*b.y+y*b.x)%mod,y*b.y%mod);}
    }dp[maxn],f[maxn],o,r,t,inv,finv;
    
    ll work() {
        if(K==0) return -1;
        if(K==1&&n!=1) return -1;
        dp[0]=T(0,0);
        ll x; T p;
        For(i,1,n-1) {
            dp[i]=T(0,1)+dp[max(i+1-K,(ll)0)];
        }
        dp[n]=T(0,1)+dp[max(n-K,(ll)0)];
        return dp[P].y;
    }	
    
    ll solve() {
        if(P==0) return 0;
        if(n==1&&K==0) return -1;
        ll x; T p;
        o=T(0,m+1); r=T(0,m); inv=T(0,qp(qp(m+1,K),mod-2));
        f[0]=T(0,1); x=min(K,n);
        For(i,1,x) f[i]=f[i-1]*T(0,(K-i+1)*qp(i,mod-2)%mod);
        For(i,0,x) f[i]=f[i]*inv*T(0,qp(m,K-i)); 
        if(m==0) return work();
        finv=T(0,qp(f[0].y,mod-2));
        if(f[0].y==1||f[0].y==0) return -1;
    //	cerr<<clock()<<"
    ";
        //f[i]: probability of get i of K , finv : inv of f[0]
        dp[0]=T(0,0);
        dp[1]=T(1,0);
        //dp[i+1]=((m+1)(dp[i]-1)-m*sum(f[j]*dp[i-j])-sum(f[j]*dp[i-j+1]))/f[0]
        For(i,1,n-1) {
            dp[i+1]=o*dp[i]-o;
            x=min((ll)i,K);
            p=T(0,0);
            For(j,0,x) p=p+f[j]*dp[i-j];
            dp[i+1]=dp[i+1]-p*r;
            x=min((ll)i+1,K);
            For(j,1,x) dp[i+1]=dp[i+1]-f[j]*dp[i-j+1];
            dp[i+1]=dp[i+1]*finv;
        }
    //	cerr<<clock()<<"
    ";
        //(1-f[0])*dp[n]=sum(f[j]*dp[n-j])+1;
        t=T(0,1); x=min(n,K);
        For(j,1,x) t=t+f[j]*dp[n-j];
        t=t*T(0,qp(mo(mod+1-f[0].y),mod-2));
        t=t-dp[n];
        if(t.x==0&&dp[P].x!=0) return -1;
        if(t.x) X=(mod-1)*t.y%mod*qp(t.x,mod-2)%mod;
        return (X*dp[P].x%mod+dp[P].y)%mod;
    }
    
    int main() {
        read(Td);
        while(Td--) {
            read(n); read(P); read(m); read(K);
            printf("%lld
    ",solve());
        }
    //	cerr<<clock()<<"
    ";
        return 0;
    }
    

     

  • 相关阅读:
    JS设置CSS样式的几种方式
    jquery基础
    JS里面的两种运动函数
    JavaScript必须了解的知识点总结。
    JavaScript调用函数的方法
    纯CSS完成tab实现5种不同切换对应内容效果
    Web设计师值得收藏的10个jQuery特效
    jQuery的.bind()、.live()和.delegate()之间区别
    Web前端:11个让你代码整洁的原则
    js函数中参数的传递
  • 原文地址:https://www.cnblogs.com/Serene-shixinyi/p/8922787.html
Copyright © 2011-2022 走看看