题解:
这个题,开始本着拿50分去写,结果写完了在vijos和tyvj上提交竟然都得了80分而且速度好像都很快,
然后调试了一些奇怪的bug(坑),就AC了。事后又想了几分钟才想明白复杂度为什么是对的QwQ我真是太弱辣自己写的正解都不知道QAQ
首先,按照题意预处理dist[i][j](就是i,j的距离),to[i][j]表示从i到j下一步要走的编号最小的点。
然后dp[s][t]记作答案。
首先如果dist[s][t]==0那么dp[s][t]=0显然。
否则如果dist[s][t]<=2那么dp[s][t]=1显然。
否则,因为是s先走,走到了to[to[s][t]][t],记作x,然后t等概率走到P(t)和t。(P(t)就是t周围的点的集合)
所以dp[s][t]=sigma{dp[x][y]}/(|P|+1)+1。其中y=t或者y∈P(t)。
这样记忆搜索一下就好了。
事实上注意到dist(s,t)随着递归在变小(因为一个走两步,一个走一步,距离肯定在减小),也可以像区间dp一样递推。
但是这个题有几个坑点(对本蒟蒻来讲)
第一个坑,要求编号最小(不知道有几个没看见的QwQ)
第二个坑,不能直接计算出走两步到达的最小编号,因为它是一步一步走的。
第三个坑,求的是期望时间而不是期望步数QwQ
最后分析一下复杂度,状态是O(N^2)的显然。
递推貌似最坏可以使O(N)的?所以复杂度是O(N^3)?
并不是,实际上每个点作为答案被统计了O(这个点的度数+1)=O(这个点相连的边的数量+1)。
然后发现每条边被统计了两次,所以第一部分复杂度是O(2*边数)=O(M)的
那个+1由于有n个点所以+1的部分是O(N)的。
所以复杂度是O(N^2+M)的。
代码:
//BZOJ 1415
//NOI 2005
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#define MAXN 1010
using namespace std;
double dp[MAXN][MAXN];
bool calc[MAXN][MAXN],vis[MAXN];
int dist[MAXN][MAXN],to[MAXN][MAXN];
vector<int> g[MAXN];queue<int> q;
int bfs(int s)
{
memset(vis,false,sizeof(vis));
while(!q.empty()) q.pop();
vis[s]=true;to[s][s]=s;
dist[s][s]=0;q.push(s);
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=g[x].size()-1;i>=0;i--)
if(!vis[g[x][i]])
{
int t=g[x][i];vis[t]=true;
dist[s][t]=dist[s][x]+1;
if(dist[s][t]<=1) to[s][t]=t;
else to[s][t]=to[s][x];
q.push(t);
}
else{
int t=g[x][i];
if(dist[s][t]<=1) continue;
if(dist[s][t]!=dist[s][x]+1) continue;
to[s][t]=min(to[s][t],to[s][x]);
}
}
return 0;
}
double getdp(int x,int y)
{
if(calc[x][y]) return dp[x][y];
calc[x][y]=true;
if(dist[x][y]==0) return dp[x][y]=0.0;
if(dist[x][y]<=2) return dp[x][y]=1.0;
dp[x][y]=getdp(to[to[x][y]][y],y);
for(int i=g[y].size()-1;i>=0;i--)
dp[x][y]+=getdp(to[to[x][y]][y],g[y][i]);
return dp[x][y]=dp[x][y]/(1.0+g[y].size())+1.0;
}
int main()
{
int n,m,s,t;scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++)
{
int u,v;scanf("%d%d",&u,&v);
g[u].push_back(v);g[v].push_back(u);
}
for(int i=1;i<=n;i++) bfs(i);
printf("%.3f\n",getdp(s,t));
return 0;
}