1 条题解

  • 0
    @ 2025-8-24 21:46:46

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Siyuan
    Dream OIer.

    搬运于2025-08-24 21:46:46,当前版本为作者最后更新于2019-02-08 11:12:30,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    $$\Large\texttt{My Blog}$$


    Description

    题目链接:Luogu 3232

    有一个无向简单连通图,顶点从 11 编号到 nn,边从 11 编号到 mm

    小 Z 在该图上进行随机游走,初始时小 Z 在 11 号顶点,每一步小 Z 以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 Z 到达 nn 号顶点时游走结束,总分为所有获得的分数之和的,答案保留 33 位小数。

    现在,请你对这 mm 条边进行编号,使得小Z获得的总分的期望值最小。

    数据范围:2n5002\le n\le 500


    Solution

    由于没有对 mm 的范围进行限定,那么 mm 的最大值可以达到 O(n2)O(n^2),这是无法接受的,因此我们考虑先统计点的期望次数

    我们设 degideg_i 表示第 ii 个点的度数,fif_i 表示第 ii 个点期望经过次数:

    $$f_i=\begin{cases}f_1=\sum_{(i,j)\in E,j\neq n} \frac{f_j}{deg_j}+1 & i=1\\f_i=\sum_{(i,j)\in E,j\neq n} \frac{f_j}{deg_j} & 1<i<n\end{cases} $$

    由于 nn 点时就停止游走了,因此不能考虑 nn 点的贡献。接下来我们对 n1n-1fif_i 进行高斯消元求解。

    我们设 gig_i 表示第 ii 条边期望经过次数:

    $$g_i=\frac{f_u}{d_u}+\frac{f_v}{d_v}\quad E_i=(u,v),u\neq n, v\neq n $$

    排序贪心,期望越大的边标号越小。

    时间复杂度O(n3)O(n^3)


    Code

    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    const int N=505,M=5e5+5;
    int n,m,tot,lnk[N],ter[M],nxt[M],st[M],ed[M],deg[N];
    double a[N][N],b[N],x[N],f[M];
    
    void add(int u,int v) {
    	ter[++tot]=v,nxt[tot]=lnk[u],lnk[u]=tot;
    }
    void Gauss(int n) {
    	for(int i=1;i<=n;++i) {
    		int p=i;
    		for(int k=i+1;k<=n;++k) if(fabs(a[k][i])>fabs(a[p][i])) p=k;
    		if(i!=p) std::swap(a[i],a[p]),std::swap(b[i],b[p]);
    		for(int k=i+1;k<=n;++k) {
    			double d=a[k][i]/a[i][i];
    			b[k]-=d*b[i];
    			for(int j=i;j<=n;++j) a[k][j]-=d*a[i][j];
    		}
    	}
    	for(int i=n;i>=1;--i) {
    		for(int j=i+1;j<=n;++j) b[i]-=x[j]*a[i][j];
    		x[i]=b[i]/a[i][i];
    	}
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;++i) {
    		scanf("%d%d",&st[i],&ed[i]);
    		add(st[i],ed[i]),add(ed[i],st[i]);
    		++deg[st[i]],++deg[ed[i]];
    	}
    	for(int u=1;u<n;++u) {
    		a[u][u]=1.0;
    		for(int i=lnk[u];i;i=nxt[i]) {
    			int v=ter[i];
    			if(v!=n) a[u][v]=-1.0/deg[v];
    		}
    	}
    	b[1]=1;
    	Gauss(n-1);
    	for(int i=1;i<=m;++i) {
    		int a=st[i],b=ed[i];
    		if(a!=n) f[i]+=x[a]/deg[a];
    		if(b!=n) f[i]+=x[b]/deg[b];
    	}
    	std::sort(f+1,f+m+1);
    	double ans=0;
    	for(int i=1;i<=m;++i) ans+=(m-i+1)*f[i];
    	printf("%.3lf\n",ans);
    }
    
    • 1

    信息

    ID
    2305
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者