题解:BZOJ3253 改编

Solution for BZOJ3253 改编

1.题意

给出一张\(n\)个点无向图,每个点有\(p_i\)的概率在这里停留,否则均匀随机的走向相邻的点。两个人在图中的两点,直到她们在同一点相遇这个游戏才会结束。

给出若干询问,每次给定每条边的边权\(v\)和两个人的初始位置,回答游戏结束前两人期望行走距离和。

\(1\leq n\leq 22,T\leq 500,1\leq v\leq 1000000\)

2.题解

建议先做CF113D,就是题面中那道题。

下文中的状态指的是一个二元组<x,y>,表示一个人在x,另一个人在y的状态。

在CF那道题中,我们巧妙的运用了期望和概率转化的关系,即最终两个人只会在同一点相遇一次,而期望等于概率*值(这里就是相遇在这个状态的次数),使得可以通过列出线性方程组求解答案。

下面回到本题中。

首先考虑转化模型。直接去计算每个人走的期望距离是很难的,所以可以转化为每个边被经过的概率*每个边的长度,这样就可以得到每个边在当前的询问中的期望长度。

而边被经过的概率是和状态密切相关的,所以可以在构造状态的矩阵同时构造边的矩阵。

这里不固定两个人的初始状态,所以只有在本状态不动的四类分类讨论。

\(d[i]\)表示i的度数,\(f[x][y]\)表示从\(x\)状态变到\(y\)的概率(直接把期望次数视为概率)(状态矩阵),\(b[x][e]\)表示从\(x\)状态出发经过\(e\)这条边的概率(边矩阵)。

\[f[<i,j>][<i,j>]=p[i]*p[j]*f[<i,j>][<i,j>]+\sum_{(i,u)\in G}\frac{1-p[i]}{d[i]}*p[j]*f[<i,j>][<u,j>]+\sum_{(j,v)\in G}p[i]*\frac{1-p[j]}{d[j]}*f[<i,j>][<j,v>]+\sum_{(i,u)\in G}\sum_{(j,v)\in G}\frac{1-p[i]}{d[i]}*\frac{1-p[j]}{d[j]}*f[<i,j>][<u,v>] \]

边矩阵的公式太难打了……,不过看代码应该很清晰。

特别的,需要判断一下\(u==v\)这样的状态,这些状态是游戏的结束态,没有向其他状态的转移。

总体来看,处理出来的这些矩阵,相当于是对于每个边,考虑了所有可能的初状态经过该边的概率,查询时只需要找到对应初状态的状态矩阵,以边矩阵作为已知量,找这个初状态分了其中多少概率,就得到了当前初状态每条边被经过的概率,乘上给出的边长就是答案了。

到这里可以得到一个直观的想法:每次重构待消元的矩阵,高斯消元出每条边的概率,乘上给定的边的长度。

但是这样查询的复杂度是\(O(Tn^6)\)的,不能接受。

回想高斯消元的形式,实际上是一个\(A*x=b\)(A为系数矩阵,x为未知数行向量,b为已知量列向量)。

发现每次消元的时候实际上A和b都是固定的,有没有什么能不重构矩阵、排除掉高斯消元复杂度的方法呢?

把上式变一下形:\(x=A^{-1}b\)

这时\(x\)的计算就变成了\(O(n^4)\)(注意b是列向量)。

那么只需要求出来A的逆矩阵就好了,对应到本题就是f。

矩阵求逆link

本题还有一个小技巧:因为是无向图,所以边是可逆的,只需要储存一半的状态就可以了,另一半是等效的。

到这里本题就结束了,注意卡卡常、卡卡精度就能AC了。

时间复杂度:\(O(n^6+Tn^4)\)

空间复杂度:\(O(n^6)\)

code:

#include<bits/stdc++.h>
using namespace std;
typedef double dou;
template <typename T>inline void read(T &x){

}
const int N=250;
const dou eps=1e-9; 
int n,m,T;
int link[25][25],deg[25];
int id[25][25],idx=0;
#define id(x,y) id[x][y]
dou p[25];
dou f[N][N],g[N][N],b[N][N],val[N];
void exgauss(int o){
	for(int i=1;i<=o;i++){
		int now=i;
		for(int j=i+1;j<=o;j++)
		if(fabs(f[j][i])>fabs(f[now][i])) now=j;
		for(int j=1;j<=o;j++) 
		swap(f[i][j],f[now][j]),swap(g[i][j],g[now][j]);
		for(int j=i+1;j<=o;j++){
			dou t=f[j][i]/f[i][i];
			for(int k=1;k<=o;k++){
				f[j][k]-=f[i][k]*t;
				g[j][k]-=g[i][k]*t;
			}
		}
	}
	for(int i=o;i;i--){
		dou t;
		for(int j=o;j>i;j--){
			t=f[i][j];
			for(int k=1;k<=o;k++){
				f[i][k]-=f[j][k]*t;
				g[i][k]-=g[j][k]*t;
			}
			
		}
		t=f[i][i];
		for(int j=1;j<=o;j++){
			f[i][j]/=t;g[i][j]/=t;
		}
	}
	
}
int main(){
	read(n);read(m);read(T);
	memset(link,-1,sizeof link);
	for(int i=1;i<=n;i++) link[i][i]=0;
	for(int i=1;i<=m;i++){
		int x,y;read(x);read(y);
		deg[x]++;deg[y]++;link[x][y]=link[y][x]=i;
	}
	for(int i=1;i<=n;i++) scanf("%lf",&p[i]);
	for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) id[i][j]=++idx;
	for(int i=1;i<n;i++)
	for(int j=i+1;j<=n;j++){
		f[id(i,j)][id(i,j)]=1.0;
		for(int x=1;x<=n;x++) if(link[i][x]!=-1)
		for(int y=1;y<=n;y++) if(link[j][y]!=-1){
			dou t=1.0;
			if(x==i) t*=p[x];else t*=(1.0-p[i])/(1.0*deg[i]);
			if(y==j) t*=p[y];else t*=(1.0-p[j])/(1.0*deg[j]);
			b[id(i,j)][link[i][x]]+=t;
			b[id(i,j)][link[j][y]]+=t;
			if(x!=y) f[id(i,j)][x<y?id(x,y):id(y,x)]-=t;
		}
	}
	for(int i=1;i<=idx;i++) g[i][i]=1.0;
	exgauss(idx);
	while(T--){
		for(int i=1;i<=m;i++) scanf("%lf",&val[i]);
		int x,y;read(x);read(y);if(x>y) swap(x,y);
		dou ans=0;
		if(x==y){printf("%.2lf\n",ans);continue;}
		for(int i=1;i<=idx;i++)for(int j=1;j<=m;j++)
		ans+=g[id(x,y)][i]*b[i][j]*val[j];
		printf("%.2lf\n",ans);
	}
	return 0;
}
posted @ 2022-03-10 15:01  Displace  阅读(39)  评论(0)    收藏  举报