zoukankan      html  css  js  c++  java
  • 5.19 省选模拟赛 小B的夏令营 概率 dp 前缀和优化dp

    LINK:小B的夏令营

    avatar
    avatar

    这道题是以前从没见过的优化dp的方法 不过也在情理之中.

    注意读题 千万不要像我这个sb一样 考完连题意都不知道是啥.

    一个长方形 要求从上到下联通的概率。

    容易发现 K天只是用来计算概率的 和 dp的状态无关。

    我们可以逐行 dp.

    容易设f[i][l][r]表示前i行 当前行l~r没有被摧毁的概率。

    考虑在k天之后第i行 l~r没被摧毁的概率。

    l-1在这k天被摧毁了 那么因为有序 概率为(C(k,l-1)p^{l-1}(1-p)^{k-l+1})

    对于r的那边同理。

    暴力转移 就是枚举上一层的L,R 使得L~R与l~r的交集不为空.

    整个时间复杂度为n^5.

    考虑 快速得到L~R 对 l~r的贡献 发现 求和l~r有交比较困难。

    考虑容斥 那么就是总-左端点在r之后-右端点在l之前即可。

    这样就可以优化到n^3 值得注意的是 上述两种情况其实是对等的 所以只需要求一种即可。

    inline int mul(int a,int b){return (ll)a*b%mod;}
    inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
    inline int mux(int a,int b){return a-b<0?a-b+mod:a-b;}
    const int MAXN=510,maxn=100010;
    int n,m,p,k;
    int f[2][MAXN][MAXN];
    int fac[maxn],inv[maxn],w[maxn],w1[MAXN];
    inline int ksm(int b,int p)
    {
    	int cnt=1;
    	while(p){if(p&1)cnt=mul(cnt,b);b=mul(b,b);p=p>>1;}
    	return cnt;
    }
    inline void prepare(int maxx)
    {
    	fac[0]=1;
    	rep(1,maxx,i)fac[i]=mul(fac[i-1],i);
    	inv[maxx]=ksm(fac[maxx],mod-2);
    	fep(maxx-1,0,i)inv[i]=mul(inv[i+1],i+1);
    }
    inline int C(int a,int b){return a<b?0:(ll)fac[a]*inv[b]%mod*inv[a-b]%mod;}
    int main()
    {
    	freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
    	get(n);get(m);p=(ll)read()*ksm(read(),mod-2)%mod;get(k);
    	prepare(max(n,k));
    	rep(0,min(k,m),i)w[i]=mul(mul(C(k,i),ksm(p,i)),ksm(1-p+mod,k-i));
    	int u=0;f[u][1][m]=1;
    	rep(1,n,T)
    	{
    		int ans=0;
    		rep(1,m,i)w1[i]=0;
    		rep(1,m,i)rep(i,m,j)
    		{
    			ans=add(ans,f[u][i][j]);
    			w1[j]=add(w1[j],f[u][i][j]);
    		}
    		rep(1,m,i)w1[i]=add(w1[i],w1[i-1]);
    		u=u^1;
    		rep(1,m,i)rep(i,m,j)
    		f[u][i][j]=mul(mux(mux(ans,w1[i-1]),w1[m-j]),mul(w[i-1],w[m-j]));
    	}
    	int ans=0;
    	rep(1,m,i)rep(i,m,j)ans=add(ans,f[u][i][j]);
    	put(ans);
    	return 0;
    }
    

    考虑100分的做法:

    状态的维度都是n^3的 必须要降维.

    按行划分是必要的 所以只能把左端点给省掉或者把右端点给省掉.

    我是将前者省掉 设状态f[i][j]表示到了第i行 以j为右端点的概率。

    这次需要先枚举l进行转移 实际上这个枚举可以经过预处理之后O(1)得到。

    所以 复杂度被降到了nm.

    值得注意的是 需要把状态转移方程给写清楚 然后考虑预处理哪些项来计算。

    不要像我一样迷瞪半天...

    inline int mul(int a,int b){return (ll)a*b%mod;}
    inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
    inline int mux(int a,int b){return a-b<0?a-b+mod:a-b;}
    const int MAXN=1510,maxn=100010;
    int n,m,p,k;
    int f[MAXN][MAXN];//f[i][j]表示前i行 右端点在j的概率.
    int fac[maxn],inv[maxn],w[maxn],w1[maxn],w2[maxn];
    inline int ksm(int b,int p)
    {
    	int cnt=1;
    	while(p){if(p&1)cnt=mul(cnt,b);b=mul(b,b);p=p>>1;}
    	return cnt;
    }
    inline void prepare(int maxx)
    {
    	fac[0]=1;
    	rep(1,maxx,i)fac[i]=mul(fac[i-1],i);
    	inv[maxx]=ksm(fac[maxx],mod-2);
    	fep(maxx-1,0,i)inv[i]=mul(inv[i+1],i+1);
    }
    inline int C(int a,int b){return a<b?0:(ll)fac[a]*inv[b]%mod*inv[a-b]%mod;}
    int main()
    {
    	freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
    	get(n);get(m);p=(ll)read()*ksm(read(),mod-2)%mod;get(k);
    	prepare(max(n,k));
    	rep(0,min(k,m),i)w[i]=mul(mul(C(k,i),ksm(p,i)),ksm(1-p+mod,k-i));
    	f[0][m]=1;
    	rep(1,n,i)
    	{
    		int ww1=0;int cnt=0;int cc=0;
    		rep(1,m,j)
    		{
    			cnt=add(cnt,f[i-1][j]);
    			ww1=add(ww1,f[i-1][j]);
    			w1[j]=add(w1[j-1],mul(w[j],ww1));
    			w2[j]=ww1;
    		}
    		rep(1,m,j)
    		{
    			cc=add(cc,w[j-1]);
    			f[i][j]=mux(mux(mul(cc,cnt),w1[j-1]),mul(cc,w2[m-j]));
    			f[i][j]=mul(f[i][j],w[m-j]);
    		}
    	}
    	int ans=0;
    	rep(1,m,i)ans=add(ans,f[n][i]);
    	put(ans);
    	return 0;
    }
    
  • 相关阅读:
    MySQL数据库之数据类型
    MySQL数据库之数据操作
    MySQL数据库之表的操作
    十、原子操作
    九、std::async异步线程
    八、条件变量
    cisco笔试记录
    七、单例设计模式
    基于HTTP的功能追加协议
    使用栈来计算后缀表达式
  • 原文地址:https://www.cnblogs.com/chdy/p/12924492.html
Copyright © 2011-2022 走看看