XII.[NOI2005]聪聪与可可
这题一个naive的思路是设\(p_{i,j}\)表示\(i\)时刻老鼠在位置\(j\)的概率,然后求出\(f_i\)表示猫\(i\)时刻前抓到老鼠的概率(因为如果\(i\)时刻猫可以抓到老鼠,则\(i+1\)时刻猫一定仍可以抓到老鼠;而\(i\)时刻猫能抓到老鼠的位置只有可能距猫的起点\(\leq 2i\)),最后差个分就能得出答案。
但是我们发现猫并不能保证所有\(\leq 2i\)的位置都能抓到——举例子来说,我们有这样一个样例:
猫从\(1\)出发,第一时刻到达\(3\);但是,如果老鼠此时走到了\(5\),虽然\(1\)到\(5\)的距离\(\leq 2i=2\),但是猫仍不能一次抓到老鼠。
所以我们不得不考虑一种新的想法,即记录下猫和鼠的位置,求出此时它期望多少轮能够捉到鼠。
我们设\(f_{i,j}\)表示猫在\(i\)时,鼠在\(j\)时的期望次数;我们再设\(nex2_{i,j}\)表示此时猫下一步会走到哪里。\(nex2\)可以通过\(O(nm)\)地预处理出来距离数组,再\(O(nm)\)地预处理出来\(nex1\)数组表示猫走一步的目标,然后通过\(nex1\)预处理出来\(nex2\)得出。
则有:
\(f_{i,j}=0\),如果\(i=j\);
\(f_{i,j}=1\),如果猫能直接从\(i\)走到\(j\);
否则,设这里有一条边\((j,k)\),则有
\[f_{i,j}=\dfrac{f_{nex2_{i,j},j}\sum f_{nex2_{i,j},k}}{deg_k+1}
\]
凭直觉发现这不可能有环;故直接记忆化搞掉即可。
时间复杂度\(O(nm)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,dis[1010][1010],nex1[1010][1010],nex2[1010][1010],cat,mouse;
double f[1010][1010];
bool vis[1010][1010];
vector<int>v[1010];
void bfs(int S){
queue<int>q;
memset(dis[S],0x3f,sizeof(dis[S])),dis[S][S]=0;
q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(auto y:v[x])if(dis[S][y]==0x3f3f3f3f)dis[S][y]=dis[S][x]+1,q.push(y);
}
}
double dfs(int x,int y){
if(vis[x][y])return f[x][y];
vis[x][y]=true;
double &now=f[x][y];
if(x==y)return now=0;
if(nex2[x][y]==-1)return now=1;
x=nex2[x][y];
for(auto z:v[y])now+=dfs(x,z)+1;
now+=dfs(x,y)+1;
now/=int(v[y].size()+1);
return now;
}
int main(){
scanf("%d%d",&n,&m);
scanf("%d%d",&cat,&mouse);
for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
for(int i=1;i<=n;i++)bfs(i);
// for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%d ",dis[i][j]);puts("");}
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
if(dis[i][j]==0x3f3f3f3f)continue;
if(dis[i][j]<=1){nex1[i][j]=-1;continue;}
int mp=-1;
for(auto k:v[i])if(mp==-1||dis[mp][j]>dis[k][j]||dis[mp][j]==dis[k][j]&&mp>k)mp=k;
nex1[i][j]=mp;
}
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
if(dis[i][j]==0x3f3f3f3f)continue;
if(nex1[i][j]==-1||nex1[nex1[i][j]][j]==-1){nex2[i][j]=-1;continue;}
nex2[i][j]=nex1[nex1[i][j]][j];
}
// for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%d ",nex2[i][j]);puts("");}
// for(int i=1;i<=n;i++)printf("%d ",dis[i]);puts("");
printf("%.3lf\n",dfs(cat,mouse));
return 0;
}