[CSGRound3]出游
链接:https://www.luogu.com.cn/problem/P6016
题目描述:有\(n\)位同学,每一位同学在第\(0\)天时有\(p_{i}\)的概率报名参加活动,若在第\(t-1\)天有一个同学的好朋友报名参加活动,那么第\(t\)天他就会报名参加活动。求截止到\(T\)天,报名参加活动的概率期望人数是多少?
题解:首先这个题目不能直接递推,因为\(“截止到\)T\(天,报名参加活动的概率期望人数”\)实际上是在取概率期望的\(“并”\)。直接递推会算重,所以不能直接递推。
考虑如何将概率期望的\(“并”\)转化,我们发现求\(0,1\)的\(“并”\)非常简单的,因为\(0,1\)的\(“并”\)其实就是或,所以我们可以枚举第\(0\)天选手参加的状态,这样算不会算重。而且矩阵“或”运算满足结合律,可以矩阵快速幂优化。
但枚举状态是\(O(2^n)\)的,考虑优化。我们发现,每一个状态的转移矩阵是相同的,可以将前面的转移矩阵先用矩阵快速幂求出来。这样可以不用枚举状态,只用算每一位的贡献即可。
但这样还是会超时,所以我们可以用\(bitset\)优化矩阵乘法,这样的复杂度就是\(O(\frac{n^2}{32}\times log(T))\)了。
#include<iostream>
#include<bitset>
#define mod 998244353
using namespace std;
struct node
{
bitset<501>x[501];
bitset<501>y[501];
};
node c,now,ans;
long long n,t,x,y,sum,res[501];
long long cnt;
node mul(node a,node b)
{
for (int i=1;i<=n;++i)
c.x[i]=c.y[i]=0;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
{
c.x[i][j]=(a.x[i]&b.y[j]).count()>=1;
c.y[j][i]=(a.x[i]&b.y[j]).count()>=1;
}
return c;
}
node fast_pow(node a,long long k)
{
if (k==1)
return a;
if (k&1)
return mul(fast_pow(mul(a,a),k/2),a);
else
return fast_pow(mul(a,a),k/2);
}
int main()
{
cin>>n>>t;
for (int i=1;i<=n;++i)
{
cin>>res[i]>>x;
for (int q=1;q<=x;++q)
{
cin>>y;
now.x[i][y]=1;
now.y[y][i]=1;
}
}
if (t==0)
{
for (int i=1;i<=n;++i)
cnt=(cnt+res[i])%mod;
cout<<cnt<<endl;
return 0;
}
ans=fast_pow(now,t);
for (int i=1;i<=n;++i)
{
sum=1;
for (int j=1;j<=n;++j)
if (ans.x[i][j])
sum=sum*(1-res[j])%mod;
sum=((1-sum)%mod+mod)%mod;
cnt=(cnt+sum)%mod;
}
cout<<cnt<<endl;
return 0;
}
本文来自博客园,作者:zhouhuanyi,转载请注明原文链接:https://www.cnblogs.com/zhouhuanyi/p/16983620.html

浙公网安备 33010602011771号