zoukankan      html  css  js  c++  java
  • 洛谷 P4564 [CTSC2018]假面(期望+dp)

    题目传送门

    题意:
    \(n\) 个怪物,第 \(i\) 个怪物初始血量为 \(m_i\)。有 \(Q\) 次操作:

    • 0 x u v,有 \(p=\frac{u}{v}\) 的概率令 \(m_x\)\(1\),另外 \(1-p\) 的概率啥也不干。如果 \(m_x=0\),那么这个操作无效。
    • 1 k x[1] x[2] ... x[k],在 \(x_1,x_2,\dots,x_k\)\(k\) 个怪物中的血量不为 \(0\) 的怪物中,随机选择一个怪物。问对于 \(i=1,2,\dots,k\),怪物 \(x_i\) 被选中的概率是多少。

    另外,所有操作结束后,输出 \(n\) 个数表示怪物 \(i\) 的血量的期望值。
    \(n \leq 200\)\(q \leq 2 \times 10^5\)\(1\) 操作的次数 \(\leq 10^3\)\(m_i \leq 100\)

    虽说是为了刷期望的题而来的,但其实这题的关键点不在于期望。
    我们令 \(dp_{i,j}\) 表示在当前时刻,怪物 \(i\) 的血量为 \(j\) 的概率。
    对于 \(0\) 操作而言,你就暴力修改 \(dp_{x,j}\) 的值,直接一遍背包就行了,时间复杂度 \(\mathcal O(m)\)
    本题难就难在 \(1\) 操作怎样处理。
    最 naive 的想法是,枚举每个 \(x_i\),显然 \(x_i\) 的血量是不能为 \(0\) 的。
    然后在剩下 \(k-1\) 个怪物中跑一遍背包,\(f_{i,j}\) 表示在前 \(i\) 个怪物中有 \(j\) 个血量不为 \(0\) 的怪物的概率,背包进行转移。
    答案为 \((1-dp_{x_t,0})\times (\sum_{i=0}^{k-1}f_{k,i}\times\frac{1}{i+1})\)
    式子很好理解,第一个括号表示 \(x_t\) 的血量不为 \(0\) 的概率,第二个括号枚举了血量不为 \(0\) 的怪物个数并用乘法原理计算出概率累加答案。
    但这样是 \(n^3\) 的。
    我的另一个 naive 的想法是 \(f_{i,j}\) 表示在前 \(i\) 个怪物中有 \(j\) 个血量不为 \(0\) 的怪物的概率,\(g_{i,j}\) 表示在后 \(i\) 个怪物中有 \(j\) 个血量不为 \(0\) 的怪物的概率。
    这样计算 \(f,g\) 的时间复杂度降到了 \(n^2\),但统计答案还是 \(n^3\) 的。
    这就要用到从背包中删除元素这一技巧。
    \(f_{i,j}\) 的状态转移式子为 \(f_{i,j}=p\times f_{i-1,j-1}+(1-p)\times f_{i-1,j}\)
    当删除一个元素的时候,我们其实是已知 \(f_{i,j}\)\(p\) 还原出 \(f_{i-1,j}\)
    注意到 \(f_{i-1,i}=0\),也就是说 \(f_{i,i}=p\times f_{i-1,i-1}\)
    于是我们倒着循环,用 \(f_{i-1,j}=\dfrac{f_{i,j+1}-p\times f_{i-1,j+1}}{1-p}\) 求出 \(f_{i-1,j}\)
    然后按照之前的公式累加答案即可。
    \(\mathcal O(qm+cn^2)\)
    注意预处理逆元,否则时间复杂度会多一个 \(\log\)(CSP-S 2020 的悲惨回忆)

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    bool chkmin(int &x,int y){return ((x>y)?(x=y,1):0);}
    bool chkmax(int &x,int y){return ((x<y)?(x=y,1):0);}
    const int MOD=998244353;
    const int MAXN=200+5;
    const int MAXM=100+5;
    int n,m,x[MAXN];
    ll dp[MAXN][MAXM],iv[MAXN],tmp[MAXN],f[MAXN][MAXN],inv[MAXN];
    ll qpow(ll x,int e){
    	ll ret=1;
    	while(e){
    		if(e&1) ret=ret*x%MOD;
    		x=x*x%MOD;e>>=1;
    	} return ret;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		int x;scanf("%d",&x);dp[i][x]=1;chkmax(m,x);iv[i]=1;
    	}
    	for(int i=1;i<=n;i++) inv[i]=qpow(i,MOD-2);
    	int Q;scanf("%d",&Q);while(Q--){
    		int opt;scanf("%d",&opt);
    		if(opt==0){
    			int x,u,v;scanf("%d%d%d",&x,&u,&v);
    			ll p=1ll*u*qpow(v,MOD-2)%MOD;
    			memset(tmp,0,sizeof(tmp));
    			for(int i=0;i<=m;i++){
    				if(i==0) tmp[i]=(tmp[i]+dp[x][i])%MOD;
    				else{
    					tmp[i]=(tmp[i]+dp[x][i]*(1-p+MOD)%MOD)%MOD;
    					tmp[i-1]=(tmp[i-1]+dp[x][i]*p%MOD)%MOD;
    				}
    			}
    			for(int i=0;i<=m;i++) dp[x][i]=tmp[i];
    			iv[x]=qpow(1-dp[x][0]+MOD,MOD-2);
    		} else {
    			int k;scanf("%d",&k);
    			for(int i=1;i<=k;i++) scanf("%d",&x[i]);
    			memset(f,0,sizeof(f));f[0][0]=1;
    			for(int i=1;i<=k;i++){
    				for(int j=0;j<=i;j++){
    					f[i][j]=f[i-1][j]*dp[x[i]][0]%MOD;
    					if(j) f[i][j]=(f[i][j]+f[i-1][j-1]*(1-dp[x[i]][0]+MOD)%MOD)%MOD;
    				}
    			}
    //			for(int i=0;i<=k;i++) printf("%lld ",f[k][i]);printf("\n");
    			for(int i=1;i<=k;i++){
    				ll ans=0;memset(tmp,0,sizeof(tmp));
    				for(int j=k-1;~j;j--)
    					tmp[j]=(f[k][j+1]-tmp[j+1]*dp[x[i]][0]%MOD+MOD)%MOD*iv[x[i]]%MOD;
    //				for(int j=0;j<=k;j++) printf("%lld ",tmp[j]);
    //				printf("\n");
    				for(int j=0;j<k;j++)
    					ans=(ans+(tmp[j]*(1-dp[x[i]][0]+MOD)%MOD)*inv[j+1]%MOD)%MOD;
    				printf("%lld ",ans);
    			} printf("\n");
    		}
    	}
    	for(int i=1;i<=n;i++){
    		ll ans=0;
    		for(int j=0;j<=m;j++) ans=(ans+1ll*j*dp[i][j]%MOD)%MOD;
    		printf("%lld ",ans);
    	} printf("\n");
    	return 0;
    }
    
  • 相关阅读:
    需学习
    CentOS中一些基本的操作记录
    允许IIS下载无后缀文件及“请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理。”的解决方法
    sql server使用的注意点及优化点 自备
    kali 系列学习02
    kali 系列学习01
    运维自动化之13
    运维自动化之12
    运维自动化之11
    运维自动化之10
  • 原文地址:https://www.cnblogs.com/ET2006/p/P4564.html
Copyright © 2011-2022 走看看