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;
    }
    
  • 相关阅读:
    图像检索(image retrieval)- 11
    图像检索(image retrieval)- 10相关
    Mock.js简易教程,脱离后端独立开发,实现增删改查功能
    Azure Monitor (3) 对虚拟机磁盘设置自定义监控
    Azure Monitor (1) 概述
    Azure SQL Managed Instance (2) 备份SQL MI
    Azure Virtual Network (17) Private Link演示
    Azure Virtual Network (16) Private Link
    Azure Virtual Network (15) Service Endpoint演示
    Azure Virtual Network (14) Service Endpoint服务终结点
  • 原文地址:https://www.cnblogs.com/ET2006/p/P4564.html
Copyright © 2011-2022 走看看