测试地址:假面
做法:本题需要用到概率DP。
首先,注意到血量很小,因此对于第一种操作,直接概率DP维护敌方单位在各个血量的概率即可,式子很简单相信大家都会,我就不写了,时间复杂度为
O(Qm)
O
(
Q
m
)
。
接下来,对于第二种操作,令第
i
i
个单位的存活概率为,则有:
ansi=∑k−1j=01j+1g(i,j)
a
n
s
i
=
∑
j
=
0
k
−
1
1
j
+
1
g
(
i
,
j
)
其中
g(i,j)
g
(
i
,
j
)
为除
i
i
之外恰有个单位存活的概率。我们怎么计算这个概率呢?注意到这些单位的安排顺序并不影响
g
g
的值,因此我们要算第个单位时,就把这个单位放到队列的最后,然后对前面进行概率DP。令
f(i,j)
f
(
i
,
j
)
为前
i
i
个单位恰有个存活的概率,则有:
f(i,j)=pif(i−1,j−1)+(1−pi)f(i−1,j)
f
(
i
,
j
)
=
p
i
f
(
i
−
1
,
j
−
1
)
+
(
1
−
p
i
)
f
(
i
−
1
,
j
)
显然
g(i,j)=f(k−1,j)
g
(
i
,
j
)
=
f
(
k
−
1
,
j
)
。那么我们对每个单位都进行一次这样的DP,总的时间复杂度就是
O(Cn3)
O
(
C
n
3
)
的,可以拿到
70
70
分。
要进一步优化,首先要发现
f(k,j)
f
(
k
,
j
)
无论是在哪个单位的计算过程中都是相同的,而
f(k,j)
f
(
k
,
j
)
一层仅由
f(k−1,j)
f
(
k
−
1
,
j
)
一层转移而来,那么我们可以尝试按照前面的状态转移方程倒推,于是有:
f(k−1,j)=f(k,j)−pkf(k−1,j−1)1−pk
f
(
k
−
1
,
j
)
=
f
(
k
,
j
)
−
p
k
f
(
k
−
1
,
j
−
1
)
1
−
p
k
于是对于每个单位的计算,时间复杂度就从
O(n2)
O
(
n
2
)
优化到了
O(n)
O
(
n
)
。然而肯定有同学注意到了,万一
pk=1
p
k
=
1
就不能使用上面的式子了,那要怎么办呢?注意到此时,有
f(k,j)=f(k−1,j−1)
f
(
k
,
j
)
=
f
(
k
−
1
,
j
−
1
)
,也就是说
f(k−1,j)=f(k,j+1)
f
(
k
−
1
,
j
)
=
f
(
k
,
j
+
1
)
,直接计算即可。上面的所有递推式都没有写边界条件,相信大家可以自行脑补(实在不行就请看本人的代码…)。那么我们就得到了一个总时间复杂度为
O(Cn2)
O
(
C
n
2
)
的算法,可以通过此题。
我傻逼的地方:有个地方忘记取模,导致计算溢出……这应该是很低级的错误了……
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
int n,m[210],q,pos[210];
ll inv[210]={0},t[210][110]={0},f[210][210]={0},g[210]={0};
ll power(ll a,ll b)
{
ll s=1,ss=a;
while(b)
{
if (b&1) s=s*ss%mod;
ss=ss*ss%mod;b>>=1;
}
return s;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&m[i]);
t[i][m[i]]=1;
inv[i]=power(i,mod-2);
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int op,id,k;
ll u,v;
scanf("%d",&op);
if (!op)
{
scanf("%d%lld%lld",&id,&u,&v);
ll p=u*power(v,mod-2)%mod;
t[id][0]=(t[id][0]+p*t[id][1])%mod;
for(int j=1;j<=m[id];j++)
t[id][j]=(((1ll-p)*t[id][j]+p*t[id][j+1])%mod+mod)%mod;
}
else
{
scanf("%d",&k);
f[0][0]=1;
for(int j=1;j<=k;j++)
{
scanf("%d",&pos[j]);
f[j][0]=f[j-1][0]*t[pos[j]][0]%mod;
for(int p=1;p<=j;p++)
f[j][p]=((f[j-1][p]*t[pos[j]][0]+f[j-1][p-1]*(1ll-t[pos[j]][0]))%mod+mod)%mod;
}
for(int j=1;j<=k;j++)
{
ll ans=0;
if (t[pos[j]][0])
{
ll nowinv=power(t[pos[j]][0],mod-2);
g[0]=f[k][0]*nowinv%mod;
for(int p=1;p<k;p++)
g[p]=((f[k][p]-g[p-1]*(1ll-t[pos[j]][0]))%mod+mod)%mod*nowinv%mod;
}
else
{
for(int p=0;p<k;p++)
g[p]=f[k][p+1];
}
for(int p=0;p<k;p++)
ans=(ans+inv[p+1]*g[p])%mod;
printf("%lld ",(ans*(1ll-t[pos[j]][0])%mod+mod)%mod);
}
printf("\n");
}
}
for(int i=1;i<=n;i++)
{
ll ans=0;
for(ll j=1;j<=m[i];j++)
ans=(ans+j*t[i][j])%mod;
printf("%lld ",ans);
}
return 0;
}