【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;
}
posted @ 2021-11-19 12:42  stoorz  阅读(52)  评论(0编辑  收藏  举报