• 【洛谷P6016】出游


    题目

    题目链接:https://www.luogu.com.cn/problem/P6016
    学校组织了一次暑期出游活动,报名将在第 \(T\) 天截止。
    一共有 \(n\) 位同学,第 \(i\) 位同学有 \(a_i\) 位朋友。朋友关系是单向的,换句话说,小 Z 有一个朋友是小 Y,并不意味着小 Y 一定也有一个朋友是小 Z。另外,自己也可能是自己的朋友。
    \(0\) 天时,每位同学会决定自己是否参加活动。第 \(i\) 位同学有 \(p_i\) 的概率决定参加,\(1-p_i\) 的概率决定不参加。
    接下来的 \(T\) 天里,每位同学会重新决定自己是否参加活动。第 \(i\) 位同学这一天决定参加活动,当且仅当至少有一个他的朋友在前一天决定参加,否则便不参加。
    你需要求出参加活动的同学人数期望,答案对 \(998244353\) 取模。

    思路

    我们设\(f[k][i][j]\)表示在第\(k\)天,同学\(i\)的选择是否会影响到同学\(j\)的选择。那么明显有转移

    \[f[k][i][j]=(f[k-1][i][1]\texttt{ and } f[k-1][1][j])\texttt{ or ... or}(f[k-1][i][n]\texttt{ and } f[k-1][n][j]) \]

    时间复杂度\(O(Tn^3)\),矩阵乘法优化后\(O(n^3\log T)\)
    由于上述转移包含位运算,所以我们可以用\(bitset\)优化,如下

    struct matrix
    {
    	bitset<N> a[N];
        //a.a[i][j]表示在当前步数下,i是否对j做贡献
    	
    	friend matrix operator *(matrix &a,matrix &b)
    	{
    		matrix c;
    		for (int i=1;i<=n;i++)
    			for (int j=1;j<=n;j++)
    				if (a.a[i][j]) c.a[i]|=b.a[j];
                    //如果在当前步数下,i对j有贡献,那么在下一步,i就能对这一步j能做贡献的点做贡献
    		return c;
    	}
    };
    

    这样的话时间复杂度就降到了\(O(\frac{n^3\log T}{32})\),可以过掉本题。
    我们处理出\(T\)步之后的贡献后,得到一个矩阵\(f\)。再枚举两个点\(i,j\),如果\(f.a[j][i]=1\),那么\(j\)就对\(i\)\(T\)天之后有贡献。
    那么如何计算\(T\)天后一个人去的期望呢?
    显然有

    \[1-q[i]=\Pi_{f.a[j][i]=1}(1-p[i]) \]

    那么直接在\(\mod\ 998244353\)的意义下计算答案即可。

    代码

    #include <cstdio>
    #include <bitset>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=510,MOD=998244353;
    int n,m,ans,p[N];
    
    struct matrix
    {
    	bitset<N> a[N];
    	
    	friend matrix operator *(matrix &a,matrix &b)
    	{
    		matrix c;
    		for (int i=1;i<=n;i++)
    			for (int j=1;j<=n;j++)
    				if (a.a[i][j]) c.a[i]|=b.a[j];
    		return c;
    	}
    }a,f;
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1,x,y;i<=n;i++)
    	{
    		f.a[i][i]=1;
    		scanf("%d%d",&p[i],&x);
    		while (x--)
    		{
    			scanf("%d",&y);
    			a.a[y][i]=1;
    		}
    	}
    	for (;m;m>>=1,a=a*a)
    		if (m&1) f=f*a;
    	for (int i=1;i<=n;i++)
    	{
    		int x=1;
    		for (int j=1;j<=n;j++)
    			if (f.a[j][i])
    				x=1LL*x*(1-p[j])%MOD;
    		ans=(ans+1-x)%MOD;
    	}
    	printf("%d",(ans%MOD+MOD)%MOD);
    	return 0;
    }
    
  • 相关阅读:
    AES对称加密和解密
    Akka并发编程框架 -概念模型(Akka.net或者Orleans)
    .net经典书籍
    计算机专业经典著作(转载)
    windows创建定时任务执行python脚本
    数据库中为什么不推荐使用外键约束(转载)
    《SQL Server性能调优实战》知识点汇总
    数据库索引知识汇总
    ASP.NET常见异常处理示例
    MVC的HTTP请求处理过程(IIS应用程序池、CLR线程池)
  • 原文地址:https://www.cnblogs.com/stoorz/p/12240579.html
Copyright © 2020-2023  润新知