P4564 [CTSC2018]假面

题意#

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

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

Solution#

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

dpi,j=dpi,j+1×p+dpi,j×(1p)

这样每次操作更新是 O(mi) 的。2×107 也不是不能跑。

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

dpi,0=dpi,1×p+dpi,0

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

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

j=1kfi,jj

于是我们需要求出 fi,j。暴力做可以到 O(n3)。考虑优化。

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

gj=fi,j+fi,j+1×1qq

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

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 @   ZCETHAN  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
历史上的今天:
2021-11-10 [HNOI2011]卡农
2021-11-10 [HNOI2013]数列
点击右上角即可分享
微信分享提示
主题色彩