• 【CF446D】DZY Loves Games


    题目

    题目链接:https://codeforces.com/contest/446/problem/D
    有一张 \(n\) 个点 \(m\) 条边的无向图,有一些点有陷阱(保证点 \(1\) 没有,点 \(n\) 有)。最开始有 \(k\) 条命,每次走到一个有陷阱的点就会减少一条命。每次会从当前点等概率随机选择一条出边走。求从点 \(1\) 开始,某一时刻到达点 \(n\) 后恰好只有 \(1\) 条命的期望。注意点 \(n\) 可以多次到达。
    \(n\leq 500\)\(m\leq 10^5\)\(k\leq 10^9\),有陷阱的点数 \(\leq 101\)

    思路

    如果所有点都是陷阱点,那么矩阵乘法搞一下就可以了。这启发我们求出从陷阱点 \(i\) 走到陷阱点 \(j\),中间不经过其他陷阱点的期望。
    考虑一个有陷阱的点 \(x\),记 \(f_i\) 表示点 \(i\) 到达点 \(x\),中间没有经过除了 \(x\) 以外的陷阱点的期望。
    那么如果 \(i\) 不是陷阱点,有

    \[f_i=\sum_{(i,j)\in E}\frac{f_j}{\text{deg}[i]} \]

    如果 \(i\)\(x\) 以外的陷阱点,则 \(f_i=0\)
    如果 \(i=x\),则 \(f_i=1\)
    然后跑高斯消元即可。
    但是很显然不可能对每一个陷阱点都跑一次高斯消元。注意到对于不同的陷阱点,只有陷阱点的常数项是有变化的,矩阵内其他元素都是不变的。
    设有 \(cnt\) 个陷阱点,可以把原来的常数项改为一个 \(cnt\) 维的向量,其中第 \(i\) 维就表示第 \(i\) 个陷阱点的期望。然后只需要跑一次高斯消元就可以了。
    最后跑矩阵快速幂即可。
    时间复杂度 \(O(n^3+cnt^3\log k+m)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=510;
    const double eps=1e-10;
    int n,m,t,cnt,typ[N],deg[N],id[N],rk[N],G[N][N];
    double a[N][N],b[N][N];
    
    struct Matrix
    {
    	double a[N][N];
    	
    	friend Matrix operator *(Matrix a,Matrix b)
    	{
    		Matrix c;
    		for (int i=1;i<=cnt;i++)
    			for (int j=1;j<=cnt;j++) c.a[i][j]=0;
    		for (int i=1;i<=cnt;i++)
    			for (int j=1;j<=cnt;j++)
    				for (int k=1;k<=cnt;k++)
    					c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]);
    		return c;
    	}
    }f,c;
    
    Matrix fpow(Matrix x,int k)
    {
    	Matrix ans;
    	for (int i=1;i<=cnt;i++)
    		for (int j=1;j<=cnt;j++) ans.a[i][j]=(i==j);
    	for (;k;k>>=1,x=x*x)
    		if (k&1) ans=ans*x;
    	return ans;
    }
    
    void gauss()
    {
    	for (int i=1;i<=n;i++)
    	{
    		for (int j=i;j<=n;j++)
    			if (fabs(a[j][i])>eps)
    			{
    				for (int k=1;k<=n;k++) swap(a[i][k],a[j][k]);
    				for (int k=1;k<=cnt;k++) swap(b[i][k],b[j][k]);
    				break;
    			}
    		for (int j=i+1;j<=n;j++)
    		{
    			double base=a[j][i]/a[i][i];
    			for (int k=1;k<=n;k++) a[j][k]-=a[i][k]*base;
    			for (int k=1;k<=cnt;k++) b[j][k]-=b[i][k]*base;
    		}
    	}
    	for (int i=n;i>=1;i--)
    		for (int j=1;j<=cnt;j++)
    		{
    			for (int k=i+1;k<=n;k++)
    				b[i][j]-=a[i][k]*b[k][j];
    			b[i][j]/=a[i][i];
    		}
    }
    
    int main()
    {
    	scanf("%d%d%d",&n,&m,&t);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&typ[i]);
    		if (typ[i]) id[i]=++cnt,rk[cnt]=i;
    	}
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		G[x][y]++; G[y][x]++; deg[x]++; deg[y]++;
    	}
    	for (int i=1;i<=n;i++)
    		if (!typ[i])
    		{
    			a[i][i]=1;
    			for (int j=1;j<=n;j++)
    				if (i!=j) a[i][j]=-1.0*G[i][j]/deg[i];
    		}
    		else a[i][i]=b[i][id[i]]=1;
    	gauss();
    	for (int i=1;i<=cnt;i++)
    	{
    		for (int j=1;j<=cnt;j++)
    			for (int k=1;k<=n;k++)
    				f.a[i][j]+=b[k][j]*G[rk[i]][k]/deg[rk[i]];
    		c.a[1][i]=b[1][i];
    	}
    	c=c*fpow(f,t-2);
    	printf("%.10f\n",c.a[1][cnt]);
    	return 0;
    }
    
  • 相关阅读:
    List Map 根据指定的字段进行去重处理
    mysql8.0免安装版详细配置
    Typora: 设置Typora自动上传图片到Github
    sed 查找行里面包含image指定的内容,在这一行的结尾添加版本内容:2.2.5
    转——visual studio快捷键大全
    OpenWrt 20.02.2 小米路由器3G配置CP1025网络打印
    PostgreSQL 的窗口函数 OVER, WINDOW, PARTITION BY, RANGE
    centos一行执行多条linux 命令
    上半年最中意的 GitHub 更新「GitHub 热点速览 v.22.21」
    《HelloGitHub》第 74 期
  • 原文地址:https://www.cnblogs.com/stoorz/p/15576662.html
Copyright © 2020-2023  润新知