题解: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\)这条边的概率(边矩阵)。
边矩阵的公式太难打了……,不过看代码应该很清晰。
特别的,需要判断一下\(u==v\)这样的状态,这些状态是游戏的结束态,没有向其他状态的转移。
总体来看,处理出来的这些矩阵,相当于是对于每个边,考虑了所有可能的初状态经过该边的概率,查询时只需要找到对应初状态的状态矩阵,以边矩阵作为已知量,找这个初状态分了其中多少概率,就得到了当前初状态每条边被经过的概率,乘上给出的边长就是答案了。
到这里可以得到一个直观的想法:每次重构待消元的矩阵,高斯消元出每条边的概率,乘上给定的边的长度。
但是这样查询的复杂度是\(O(Tn^6)\)的,不能接受。
回想高斯消元的形式,实际上是一个\(A*x=b\)(A为系数矩阵,x为未知数行向量,b为已知量列向量)。
发现每次消元的时候实际上A和b都是固定的,有没有什么能不重构矩阵、排除掉高斯消元复杂度的方法呢?
把上式变一下形:\(x=A^{-1}b\)。
这时\(x\)的计算就变成了\(O(n^4)\)(注意b是列向量)。
那么只需要求出来A的逆矩阵就好了,对应到本题就是f。
本题还有一个小技巧:因为是无向图,所以边是可逆的,只需要储存一半的状态就可以了,另一半是等效的。
到这里本题就结束了,注意卡卡常、卡卡精度就能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;
}

浙公网安备 33010602011771号