P3232 [HNOI2013 / JSOI2013] 游走 解题思路梳理
前言
- 注意!本文不是题解,仅仅是msjing为了整理思路写的,
且msjing不想再另搞一个分区了所以扔题解里了,以后写得多了再整理,咕咕咕,请前往正规题解处查看题解
solve前(?)
- 给一张无向图,一人随机
虾极霸游走,给每个点编号,让总期望最小 好提莫难搞啊啊啊啊啊啊(提莫没有招惹任何人)- 好吧有滚木个思路所以
看看题解吧 - 注:msjing看到是这篇/bx/bx/bx
solve?
- 想让期望最小,贪心地想,让走的多的边编号小就是正确的
- 问题来到:如何统计边的期望
- 令 \(g_i\) 为边 \(i\) 经过期望次数,我们发现这个与这个边的两端有关,那么我们有柿子
-
其中,\(f_x , f_y\) 表示 \(x,y\) 的经过期望次数;\(d_x,d_y\) 表示 \(x,y\) 的度
-
我们来理解一下,对于一次到达 \(x\) 并且走 \(x \to y\) 这条边的概率是 \(\frac{1}{d_x}\),所以乘上 \(f_x\),由于可以从 \(x\) 或 \(y\) 到达这条边,所以是加和
-
下一步,问题转化为:求点经过期望次数即 \(f_i\)
-
仍然按上边定义,考虑 \(f_i\) 的受影响范围
-
令要求的点为 \(x\),注意到与 \(x\) 有关的点就是 \(x\) 可以到的所有点,那么我们把他们全统计起来,有
-
其中,\(k\) 是与 \(x\) 相连的所有点的个数
-
我们再理解一下,我们已经知道每个点都和与它联通的节点有关,同上到一个点的概率为 \(\frac{1}{d_i}\),同上得到柿子
-
但是还是要有注意的,共两点
- 第 \(1\) 个点认为走过了,所以 \(f_1 = 1+\sum\limits{\frac{f_i}{d_i}}\)
- 第 \(n\) 个点就要停了,所以直接 \(f_n = 0\)
-
我们发现所有柿子加起来是 \(n-1\) 个 \(n-1\) 元一次方程组,掏出 \(\mathscr{gaussjordan}\) 消元法,直接搞一下
代码实现
- 有几个关于上述的实现
- 由于我们
把 \(n\) 踢飞了不计算 \(n\),所以把 \(n-1\) 即可,但是要注意需要用 \(n\) 的时候要加回来 - 关于消完元的排序,就是贪心了
- 输入的边要记录一下后面要用
- 由于我们
- 注意数组的大小,
msjing已经飞起来了 - 代码就随便给了,没啥的,高斯消元就是板板板
点击查看代码
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=510,maxm=125010,eps=1e-8;
long long read() {long long x=0,f=1;char ch=getchar();while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}
int n,m;
int x[maxm],y[maxm],h[maxm<<1],to[maxm<<1],nxt[maxm<<1],tot,d[maxm];
void add(int x,int y) {tot++;to[tot]=y;nxt[tot]=h[x];h[x]=tot;}
double a[maxn][maxn],g[maxm],ans;
struct g_j
{
void gauss_jordan()
{
for (int i=1;i<=n;i++)
{
int r=i;
for (int j=i;j<=n;j++) if (fabs(a[j][i])>eps) {r=j;break;}
if (r!=i) swap(a[r],a[i]);
if(fabs(a[i][i])<eps) continue;
for (int j=1;j<=n;j++)
{
if (j == i) continue;
double t=a[j][i]/a[i][i];
for (int k=i;k<=n+1;k++) a[j][k]-=t*a[i][k];
}
}
for (int i=1;i<=n;i++) a[i][n+1]/=a[i][i];
}
}G;
int main()
{
n=read()-1,m=read();for (int i=1;i<=m;i++) x[i]=read(),y[i]=read(),add(x[i],y[i]),add(y[i],x[i]),d[x[i]]++,d[y[i]]++;
a[1][n+1]=-1;
for (int i=1;i<=n;i++)
{
a[i][i]=-1;
for (int j=h[i];j;j=nxt[j])
{
int y=to[j];
if (y!=n+1) a[i][y]=1.0/d[y];
}
}
G.gauss_jordan();
for (int i=1;i<=m;i++) g[i]=a[x[i]][n+1]/d[x[i]]+a[y[i]][n+1]/d[y[i]];
sort(g+1,g+1+m);
for (int i=1;i<=m;i++) ans+=g[i]*(m-i+1);
printf("%.3lf\n",ans);
return 0;
}
后话
- 这是第114篇文了
如果你数了数发现不够那就是msjing大手发力了,哼哼哼哼哼哈啊啊啊啊啊啊,哈啊啊啊啊啊啊,啊啊啊啊啊啊啊,啊啊啊啊啊啊啊 - 附图一张

-
msjing要开始挑战随机游走了
-
这里是26.7.9的msjing,昨天
贺写完随机游走,大概理解了吧

浙公网安备 33010602011771号