loj2552 [CTSC2018] 假面 【概率dp】

题面


SOL

做法貌似挺多? (比如多项式)

一道模拟型的概率dp;
首先我们 r e a d   t h e   p r o b l e m read\ the\ problem read the problem,然后 m a k e   a   d r a f t make\ a\ draft make a draft,然后 A C AC AC

其实中间背包dp的可逆性挺妙的。(可能是我tcl。。)

对于操作1,维护每一回合每个点的每点生命值的概率。

对于操作2,先不管一定选某个点的情况,相当于做一个 0 / 1 0/1 0/1背包。然后我们需要得到必须选第 i i i点的 d p dp dp值。发现,无论 d p dp dp的顺序,最后的 f [ n ] [ i = 1... n ] f[n][i=1...n] f[n][i=1...n]的答案是一定的。

所以我们可以假设第 i i i个点最后选,用 f [ n ] [ n ] = f [ n − 1 ] [ n − 1 ] ∗ p i f[n][n]=f[n-1][n-1]*p_i f[n][n]=f[n1][n1]pi 逆推出 f [ n − 1 ] [ n − 1 ] ( 没 有 选 第 i 点 ) f[n-1][n-1](没有选第i点) f[n1][n1]i 然后再倒着推下去就行了。


CODE

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define cs const
#define ri register int
#define db double
#define lb long double
#define gc getchar()
#define in red()
inline int red(){
	int num=0,f=1;char c=gc;
	for(;!isdigit(c);c=gc)if(c=='-')f=-1;
	for(;isdigit(c);c=gc)num=num*10+(c^48);
	return num*f;
}
cs int N=210,mod=998244353;
inline int mul(cs int &a,cs int &b){return 1ll*a*b%mod;}
inline int add(cs int &a,cs int &b){return a+b>mod ? a-mod+b:a+b;}
inline int dec(cs int &a,cs int &b){return a-b<0 ? a+mod-b : a-b;}
inline int ksm(int a,int b){int ans=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ans=mul(ans,a);return ans;}

int iv[N],F[N],p[N][N],iP[N],f[N],m[N],n,Q;
vector<int>V;

inline void op1(){
	int n=V.size();
	for(ri i=1;i<=n;++i)f[i]=0;
	f[0]=1;
	for(ri id=0,i;id<n;++id){
		i=V[id];
		int P=dec(1,p[i][0]),dP=dec(1,P);
		for(ri j=n;j>=1;--j){
			f[j]=add(mul(f[j],dP),mul(f[j-1],P));
		}
		f[0]=mul(f[0],dP);
	}
	for(ri id=0,i;id<n;++id){
		i=V[id];
		int P=dec(1,p[i][0]),dP=dec(1,P),now=mul(iP[i],f[n]);F[i]=0;
		for(ri t=n;t>=1;--t){
			F[i]=add(F[i],mul(iv[t],now));	
			now=mul(iP[i],dec(f[t-1],mul(dP,now)));
		}
		F[i]=mul(F[i],P);
	}
}
inline void op0(int i,int P){	
	int dP=dec(1,P);
	p[i][0]=add(p[i][0],mul(p[i][1],P));
	for(ri j=1;j<=m[i];++j){
		p[i][j]=add(mul(dP,p[i][j]),mul(P,p[i][j+1]));
	}
	iP[i]=ksm(dec(1,p[i][0]),mod-2);
}
inline void init(){
	for(ri i=1;i<=n;++i)p[i][m[i]]=1,iP[i]=1,iv[i]=ksm(i,mod-2);
}
signed main(){
//	freopen("data.in","r",stdin);
	n=in;
	for(ri i=1;i<=n;++i)m[i]=in;
	init();
	Q=in;
	while(Q--){
		int op=in,id,u,v,k;
		if(op==0)id=in,u=in,v=in,op0(id,mul(u,ksm(v,mod-2)));
		if(op==1){
			k=in;V.clear();
			while(k--)V.push_back(in);
			op1();
			for(ri i=0,up=V.size();i<up;++i){
				cout<<F[V[i]]<<' ';
			}
			cout<<'\n';
		}
	}
	for(ri i=1;i<=n;++i){
		int ans=0;
		for(ri j=1;j<=m[i];++j){
			ans=add(ans,mul(j,p[i][j]));
		}
		cout<<ans<<' ';
	}
	cout<<'\n';
	


	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值