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


    点此看题面

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

    锁定操作

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

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

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

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

    结界操作

    这才是这道题的核心。

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

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

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

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

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

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

    [g_i'=frac{g_i-(1-f_{w,i}) imes 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," 
    "[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," 
    "[x==n]),++x)
    		for(t=0,i=1;i<=a[x];++i) t=(1LL*i*f[x][i]+t)%X;return 0;//期望=∑值×概率
    }
    
  • 相关阅读:
    Android Studio Tips -- 布局预览
    weibo sdk Multiple dex files define Lcom/sina/weibo/sdk/BuildConfig
    Android Studio下添加assets目录
    Android中bitmap图片透明度的处理(以撕美女衣服为例)
    Android中使用ViewPage进行广告轮播测试代码
    比赛现场打分管理平台概述
    排行榜每页默认为8行,如何把默认行数改成12行或其他行数?
    复习重点补充-
    字典读写训练
    Python爬网常见方法:find_all与re的结合使用
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4564.html
Copyright © 2020-2023  润新知