Loading

P4564 [CTSC2018]假面

题意

\(n\) 个敌方单位,每次操作会使其中某个单位有 \(p\) 的概率减少 \(1\) 的血量。每次询问会给出 \(k\) 个敌方单位的编号,求从这些存活的敌方单位中等概率选取一个,每个敌方单位被选取的概率。

最后输出每个敌方单位的期望血量。

Solution

小数据范围,考虑暴力维护每个敌方单位每个血量的概率。令 \(dp_{i,j}\) 表示单位 \(i\) 有血量 \(j\) 的概率。考虑对于每次操作,有:

\[dp_{i,j}=dp_{i,j+1}\times p+dp_{i,j}\times (1-p) \]

这样每次操作更新是 \(O(m_i)\) 的。\(2\times 10^7\) 也不是不能跑。

注意在 \(j=0\) 的时候,攻击会无效,所以有:

\[dp_{i,0}=dp_{i,1}\times p+dp_{i,0} \]

最后的期望很简单,直接暴力统计即可。

至于中间的询问,其实我们还是想知道这些人中,在某个人存活的情况下,存活多少个人的概率是多少。对于每次询问,不妨我们设 \(f_{i,j}\) 表示在 \(i\) 存活的情况下,有 \(j\) 个人存活的概率。这样 \(i\) 的答案就可以表示为:

\[\sum_{j=1}^k \dfrac{f_{i,j}}{j} \]

于是我们需要求出 \(f_{i,j}\)。暴力做可以到 \(O(n^3)\)。考虑优化。

我们令 \(g_{i}\) 表示任意 \(i\) 个人存活的概率,这东西可以 \(O(n^2)\) 递推得到。我们希望从中得到 \(f\)。我们令 \(i\) 存活的概率是 \(q\),则有:

\[g_j=f_{i,j}+f_{i,j+1}\times \dfrac{1-q}{q} \]

就是说任意 \(j\) 个人存活的概率等于 \(i\) 存活的情况下 \(j\) 个人存活的概率加上 \(i\) 不存活的情况下,\(j\) 个人存活的概率。\(O(n^2)\) 跑一遍还原就可以了。

Code

// Problem: 
//     P4564 [CTSC2018]假面
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4564
// Memory Limit: 500 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
#define int long long
using namespace std;
const int MOD=998244353;
int ksm(int a,int p){
	int ret=1;while(p){
		if(p&1) ret=ret*a%MOD;
		a=a*a%MOD; p>>=1;
	}return ret;
}
int iv[210];
int inv(int x){
	if(x<=200){
		if(iv[x]) return iv[x];
		else return iv[x]=ksm(x,MOD-2);
	}return ksm(x,MOD-2);
}

int m[210],dp[210][110],id[210];
int g[210],f[210][210];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n;cin>>n;
	rep(i,1,n) cin>>m[i],dp[i][m[i]]=1;
	int Q;cin>>Q;
	int op,u,v,k;
	while(Q--){
		cin>>op;
		if(op==0){
			cin>>k>>u>>v;
			int p=u*inv(v)%MOD,q=(1+MOD-p)%MOD;
			rep(i,0,m[k])
				if(i) dp[k][i]=(dp[k][i+1]*p%MOD+dp[k][i]*q%MOD)%MOD;
				else dp[k][i]=(dp[k][i+1]*p%MOD+dp[k][i])%MOD;
		}
		else{
			cin>>k;
			rep(i,1,k) cin>>id[i];
			memset(g,0,sizeof(g));
			memset(f,0,sizeof(f));
			g[0]=1;
			rep(i,1,k) per(j,i,0){
				g[j]=g[j]*dp[id[i]][0]%MOD;
				if(j) (g[j]+=g[j-1]*(1+MOD-dp[id[i]][0])%MOD)%=MOD;
			}rep(i,1,k){
				int q=(1+MOD-dp[id[i]][0])%MOD;
				int p=dp[id[i]][0];q=inv(q);
				if(!q) continue;
				f[i][k]=g[k];
				per(j,k-1,1) f[i][j]=(g[j]+MOD-f[i][j+1]*q%MOD*p%MOD)%MOD;
			}rep(i,1,k){
				int cur=0;
				rep(j,1,k)
					cur=(cur+f[i][j]*inv(j)%MOD)%MOD;
				cout<<cur<<' ';
			}cout<<'\n';
		}
	}
	rep(i,1,n){
		int cur=0;
		rep(j,1,m[i]) cur=(cur+j*dp[i][j]%MOD)%MOD;
		cout<<cur<<' ';
	}
	return 0;
}
posted @ 2022-11-10 08:24  ZCETHAN  阅读(15)  评论(0编辑  收藏  举报