[LOJ]#2552. 「CTSC2018」假面 DP

Solution

对于 o p = 0 op=0 op=0,直接维护 f i , j f_{i,j} fi,j表示第 i i i个人此时剩下 j j j点生命值的概率即可。
对于 o p = 1 op=1 op=1,需要知道每个人在存活的情况下存活 i i i个人的概率,这其实是可以DP的,用的是消失之物这道题的一个做法,设 s j s_j sj为在 k k k个人中选 j j j个存活的概率和, h i , j h_{i,j} hi,j为令第 i i i个人必定死亡的情况下选出 j j j个存活的概率和,显然 h i , 0 = s 0 h_{i,0}=s_0 hi,0=s0,然后这样递推 h i , j = s j − h i , j − 1 ÷ f i , 0 × ( ∑ j = 1 100 f i , j ) h_{i,j}=s_j-h_{i,j-1}\div f_{i,0}\times (\sum_{j=1}^{100}f_{i,j}) hi,j=sjhi,j1÷fi,0×(j=1100fi,j)
正确性显然。这个小技巧还是十分重要的,要好好记住。注意要特判一些 0 0 0的情况。

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=210,Maxm=110;
const int inf=2147483647;
const int mod=998244353;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
void upd(int&x,int y){x+=y;if(x>=mod)x-=mod;}
int n,m,f[Maxn][Maxm],s[Maxn][Maxn],h[Maxn][Maxn],a[Maxn],b[Maxn],c[Maxn],inv[Maxn],invc[Maxn];
int Pow(int x,int y)
{
	if(!y)return 1;
	int t=Pow(x,y>>1),re=(LL)t*t%mod;
	if(y&1)re=(LL)re*x%mod;
	return re;
}
int main()
{
	n=read();
	inv[0]=inv[1]=1;for(int i=2;i<=n;i++)inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=n;i++)
	for(int j=0;j<=101;j++)
	f[i][j]=0;
	for(int i=1;i<=n;i++)f[i][read()]=1;
	int Q=read();
	while(Q--)
	{
		int op=read(),k=read(),u,v;
		if(!op)
		{
			u=read(),v=read();
			int g=(LL)u*Pow(v,mod-2)%mod,gg=(1-g+mod)%mod;
			for(int i=0;i<=100;i++)f[k][i]=(LL)f[k][i]*gg%mod,upd(f[k][i],(LL)f[k][i+1]*g%mod);
		}
		else
		{
			int cnt=0;
			for(int i=1;i<=k;i++)
			{
				a[i]=read();
				b[i]=0;
				for(int j=1;j<=100;j++)upd(b[i],f[a[i]][j]);
				c[i]=(1-b[i]+mod)%mod,invc[i]=Pow(c[i],mod-2);
				if(!invc[i])cnt++;
			}
			s[0][1]=0;s[0][0]=1;
			for(int i=1;i<=k;i++)
			for(int j=0;j<=i;j++)
			{
				s[i][j]=(LL)s[i-1][j]*c[i]%mod;
				if(j)upd(s[i][j],(LL)s[i-1][j-1]*b[i]%mod);
			}
			for(int i=1;i<=k;i++)
			{
				h[i][0]=s[k][0];
				for(int j=1;j<=k;j++)
				{
					if(!invc[i])h[i][j]=0;
					else h[i][j]=(s[k][j]-(LL)h[i][j-1]*invc[i]%mod*b[i]%mod+mod)%mod;
				}
			}
			for(int i=1;i<=k;i++)
			{
				int ans=0;
				for(int j=1;j<=k;j++)upd(ans,(LL)((s[k][j]-h[i][j]+mod)%mod)*inv[j]%mod);
				printf("%d ",ans);
			}
			puts("");
		}
	}
	for(int i=1;i<=n;i++)
	{
		int ans=0;
		for(int j=1;j<=100;j++)upd(ans,(LL)f[i][j]*j%mod);
		printf("%d ",(LL)ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值