• [BJOI2018]治疗之雨


    题目

    我还没疯

    发现如果我们将血量抽象成点,一轮操作抽象成图上的一条边,我们如果能求出每一条边的概率,我们就能搞一下这道题

    假设我们求出了这个图(E),设(dp_i)表示从(i)点到达(0)点的期望路径长度

    那么我们可以列出如下的方程

    [dp_u=sum_{(u,v)in E}P(u,v) imes(dp_v+1) ]

    发现这个方程可以高斯消元来做

    问题变成了如何求出这张图

    我们如求出了经过(k)次减小的操作,血量(i)变成血量(j)的概率是多少,我们讨论一下那个增加的操作,就能把这张图求出来了

    发现(k)很大(n)较小,于是觉得可以矩阵优化

    优化个鬼啊

    (dp_{i,j})表示经过(i)次减小操作血量为(j)的概率

    显然有

    [dp_{i,j}=frac{1}{m+1} imes dp_{i-1,j+1}+frac{m}{m+1} imes dp_{i-1,j} ]

    发现这个柿子确实可以矩阵优化一下,变成(O(n^3logk))

    之后套上消元,就有(40)分了

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define maxn 205
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    const LL mod=1000000007;
    inline int read() {
        int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    void exgcd(LL a,LL b,LL &x,LL &y) {if(!b) {x=1,y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
    inline LL inv(LL t) {LL x,y;exgcd(t,mod,x,y);return (x%mod+mod)%mod;}
    int n,p,T;
    LL m,k;
    LL ans[maxn][maxn],a[maxn][maxn],b[maxn][maxn],E[maxn];
    inline void did_ans() {
        LL mid[maxn][maxn];
        for(re int i=0;i<=n;i++)
            for(re int j=0;j<=n;j++) mid[i][j]=ans[i][j],ans[i][j]=0;
        for(re int k=0;k<=n;k++)
            for(re int i=0;i<=n;i++)
                for(re int j=0;j<=n;j++)
                    ans[i][j]=(ans[i][j]+a[i][k]*mid[k][j]%mod)%mod;
    }
    inline void did_a() {
        LL mid[maxn][maxn];
        for(re int i=0;i<=n;i++)
            for(re int j=0;j<=n;j++) mid[i][j]=a[i][j],a[i][j]=0;
        for(re int k=0;k<=n;k++)
            for(re int i=0;i<=n;i++)
                for(re int j=0;j<=n;j++)
                    a[i][j]=(a[i][j]+mid[i][k]*mid[k][j]%mod)%mod;
    }
    inline void Quick(LL b) {while(b) {if(b&1) did_ans();b>>=1ll;did_a();}}
    int main() {
        T=read();
        while(T--) {
            n=read(),p=read(),m=read(),k=read();
            memset(b,0,sizeof(b));memset(ans,0,sizeof(ans));
            memset(E,0,sizeof(E));memset(a,0,sizeof(a));
            if(!p) {puts("0");continue;}
            if(!k) {puts("-1");continue;}
            if(!m&&k==1) {puts("-1");continue;}
            if(!m){
                int ans=0;
                while(p>0){if(p<n) p++;p-=k;ans++;}
                printf("%d
    ",ans); continue;
            }
            LL Inv=inv(m+1ll); 
            for(re int i=1;i<=n;i++) 
                a[i][i+1]=Inv,a[i][i]=m*Inv%mod;a[n][n+1]=0;
            a[0][0]=1,a[0][1]=Inv;
            for(re int i=0;i<=n;i++) 
                ans[i][i]=1;
            Quick(k);
            for(re int i=1;i<n;i++) {
                for(re int j=1;j<=i;j++)
                    b[i][j]+=ans[j][i]*Inv%mod*m%mod,b[j][i]%=mod;
                for(re int j=1;j<=i+1;j++)
                    b[i][j]+=ans[j][i+1]*Inv%mod,b[j][i]%=mod;
            }
            for(re int i=1;i<=n;i++) b[n][i]=ans[i][n];
            for(re int i=1;i<=n;i++) b[i][n+1]=mod-1;
            for(re int i=1;i<=n;i++) b[i][i]-=1ll,b[i][i]=(b[i][i]+mod)%mod;
            for(re int i=1;i<=n;i++) {
                LL t=inv(b[i][i]);
                for(re int j=n+1;j>=i;--j)
                    b[i][j]=(b[i][j]*t)%mod;
                for(re int j=i+1;j<=n;j++)
                    for(re int k=n+1;k>=i;--k)
                        b[j][k]=(b[j][k]-b[j][i]*b[i][k]%mod+mod)%mod;
            }
            E[n]=b[n][n+1];
            for(re int i=n-1;i;--i) {
                E[i]=b[i][n+1];
                for(re int j=i+1;j<=n;j++)
                    E[i]=(E[i]-E[j]*b[i][j]%mod+mod)%mod;
            }
            printf("%lld
    ",E[p]);
        }
        return 0;
    }
    

    显然复杂度不对,尤其是矩阵这边

    根据高中数学必修三,显然血量减少(r)的概率应该是

    [frac{inom{k}{r}m^{k-r}}{(m+1)^k} ]

    于是处理一下组合数就不用矩阵了

    现在的复杂度就只剩下高斯消元的(O(n^3))

    发现这个矩阵非常特殊,第(i)行只有前(i+1)列有值,于是我们往下消元的时候一行只需要消三个数就可以了

    复杂度(O(Tn^2))

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define maxn 1505
    #define re register
    #define LL long long
    const LL mod=1000000007;
    inline int read() {
    	int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    int n,p,T;LL m,k;
    LL ans[maxn],b[maxn][maxn],E[maxn],C[maxn];
    void exgcd(LL a,LL b,LL &x,LL &y) {if(!b) {x=1,y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
    inline LL inv(LL t) {LL x,y;exgcd(t,mod,x,y);return (x%mod+mod)%mod;}
    inline LL quick(LL a,LL b) {LL S=1;while(b) {if(b&1ll) S=S*a%mod;b>>=1ll;a=a*a%mod;}return S;}
    int main() {
    	T=read();
    	while(T--) {
    		n=read(),p=read(),m=read(),k=read();
    		memset(b,0,sizeof(b));memset(ans,0,sizeof(ans));
    		memset(E,0,sizeof(E));memset(C,0,sizeof(C));
    		if(!p) {puts("0");continue;}
    		if(!k) {puts("-1");continue;}
    		if(!m&&k==1) {puts("-1");continue;}
    		if(!m){
                int ans=0;
                while(p>0){if(p<n) p++;p-=k;ans++;}
                printf("%d
    ",ans); continue;
            }
            LL Inv=inv(m+1ll),D=inv(quick(m+1ll,k));
            C[0]=1;LL now=k,fac=1,t=k;C[1]=k;
    		for(re int i=2;i<=n;i++) t--,fac=(fac*i)%mod,now=(now*t)%mod,C[i]=now*inv(fac)%mod;
    		for(re int i=0;i<=n&&k>=i;i++) ans[i]=C[i]*quick(m,k-i)%mod*D%mod;  
    		for(re int i=1;i<n;i++) {
    			for(re int j=1;j<=i;j++)
    				b[i][j]+=ans[i-j]*Inv%mod*m%mod,b[j][i]%=mod;
    			for(re int j=1;j<=i+1;j++)
    				b[i][j]+=ans[i+1-j]*Inv%mod,b[j][i]%=mod;
    		}
    		for(re int i=1;i<=n;i++) b[n][i]=ans[n-i];
    		for(re int i=1;i<=n;i++) b[i][n+1]=mod-1;
    		for(re int i=1;i<=n;i++) b[i][i]-=1ll,b[i][i]=(b[i][i]+mod)%mod;
    		for(re int i=1;i<=n;i++) {
    			LL t=inv(b[i][i]);
    			for(re int j=n+1;j>=i;--j)
    				b[i][j]=(b[i][j]*t)%mod;
    			for(re int j=i+1;j<=n;j++) {
    					b[j][n+1]=(b[j][n+1]-b[j][i]*b[i][n+1]%mod+mod)%mod;
    					for(re int k=i+1;k>=i;k--) 
    						b[j][k]=(b[j][k]-b[j][i]*b[i][k]%mod+mod)%mod;
    				}
    		}
    		E[n]=b[n][n+1];
    		for(re int i=n-1;i;--i) {
    			E[i]=b[i][n+1];
    			for(re int j=i+1;j<=n;j++)
    				E[i]=(E[i]-E[j]*b[i][j]%mod+mod)%mod;
    		}
    		printf("%lld
    ",E[p]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    DateTime.Now.ToString("yyyy/MM/dd") 时间格式化中的MM为什么是大写的?
    新入门PGSQL数据库(尝试利用PGPOOL实现分布式),摘录笔记
    MongoDB入门教程之C#驱动操作实例
    使用MongoDB C#官方驱动操作MongoDB
    【OOAD】OOAD概述
    【OOAD】设计模式概述
    【OOAD】面向对象设计原则概述
    【OOAD】OOP的主要特征
    深入浅出设计模式——访问者模式(Visitor Pattern)
    深入浅出设计模式——模板方法模式(Template Method Pattern)
  • 原文地址:https://www.cnblogs.com/asuldb/p/10418346.html
Copyright © 2020-2023  润新知