把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷4564】[CTSC2018] 假面(有意思的DP题)

点此看题面

  • \(n\)个敌方单位,各有\(a_i\)点体力。
  • \(q\)次操作,分为两种:
    • 锁定:以\(p\)的概率对指定敌方单位造成\(1\)点伤害。
    • 结界:在给定的若干敌方单位中随机选中一个存活的单位,求每个敌方单位被选中的概率。
  • 最后你还要输出每个敌方单位剩余体力值的期望。
  • \(n\le200,a_i\le100,q\le2\times10^5\),其中结界操作次数\(\le10^3\)

锁定操作

显然,我们可以用\(f_{i,j}\)表示\(i\)剩余\(j\)点体力值的概率。

对于一次锁定操作,假设操作对象为\(x\),概率为\(y\),显然有转移方程:

\[f_{x,i}=y\times f'_{x,i+1}+(1-y)\times f'_{x,i} \]

但这里要注意,若\(i=0\),则后一项的转移系数为\(1\),因为当体力为\(0\)时不可能再失去体力。

结界操作

这才是这道题的核心。

我们设\(g_i\)\(i\)个人存活的概率,那么枚举每个人,有个很显然的转移:

\[g_i=f_{w,0}\times g'_i+ (1-f_{w,0})\times g'_{i-1} \]

好,现在我们知道了\(g_i\),问题在于怎么求出每个人的答案。

原本\(DP\)时我们考虑了每个人存活与不存活两种情况,但如果一个人能被结界选中,必须满足他存活。

所以我们要设法从\(g_i\)中消去一个人的贡献。

其实就是把我们上面的\(DP\)数组移项:

\[g_i'=\frac{g_i-(1-f_{w,i})\times g'_{i-1}}{f_{w,0}} \]

然后强制这个人存活计算概率即可。

但要注意这里\(f_{w,0}\)作为了除数,如果\(f_{w,0}=0\)怎么办?

发现此时这个人必然存活,不需消去贡献,直接计算答案即可。

代码:\(O(qm+cn^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200
#define M 100
#define C 1000 
#define X 998244353 
using namespace std;
int n,a[N+5],w[N+5],f[N+5][M+5],g[N+5],s[N+5],Inv[N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
main()
{
	RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i),f[i][a[i]]=1,Inv[i]=QP(i,X-2);
	RI Qt,op,x,y,z,t;scanf("%d",&Qt);W(Qt--)
	{
		if(scanf("%d%d",&op,&x),!op)//锁定操作
		{
			scanf("%d%d",&y,&z),y=1LL*y*QP(z,X-2)%X;//分数取模
			for(i=0;i<=a[x];++i) f[x][i]=(1LL*y*f[x][i+1]+1LL*(i?X+1-y:1)*f[x][i])%X;//转移
		}
		else//结界操作
		{
			for(memset(g,0,sizeof(g)),g[0]=y=1;y<=x;++y) for(scanf("%d",w+y),//求出整个DP数组
				i=y;~i;--i) g[i]=(1LL*f[w[y]][0]*g[i]+(i?1LL*(X+1-f[w[y]][0])*g[i-1]:0))%X;
			for(y=1;y<=x;printf("%d%c",t," \n"[y==x]),++y)//枚举每个人计算答案
				if(f[w[y]][0]) for(z=QP(f[w[y]][0],X-2),t=i=0;i<=x;++i)//消去贡献
					s[i]=1LL*z*(g[i]-(i?1LL*(X+1-f[w[y]][0])*s[i-1]%X:0)+X)%X,
					t=(1LL*(X+1-f[w[y]][0])*s[i]%X*Inv[i+1]+t)%X;//统计答案
				else for(t=0,i=1;i<=x;++i) t=(1LL*g[i]*Inv[i]+t)%X;//必然存活,直接计算
		}
	}
	for(x=1;x<=n;printf("%d%c",t," \n"[x==n]),++x)
		for(t=0,i=1;i<=a[x];++i) t=(1LL*i*f[x][i]+t)%X;return 0;//期望=∑值×概率
}
posted @ 2020-11-05 08:15  TheLostWeak  阅读(93)  评论(0编辑  收藏  举报