Loading

[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;
}
posted @ 2022-12-14 21:26  zhouhuanyi  阅读(35)  评论(0)    收藏  举报