其他分享
首页 > 其他分享> > bzoj3143 [HNOI2013]游走

bzoj3143 [HNOI2013]游走

作者:互联网

题目链接

solution

如果可以统计出每条边期望走多少次,那么只要按照经过的次数从小到大降序编号就能保证最终得分最小了。

统计每条边走的次数不好统计,但是统计点的经过次数似乎不难。那么边的经过次数就是他所连接的两点经过次数分别除以这两个点的度数之和。

然后考虑如何统计每个点的经过次数。

用\(f[i]\)表示\(i\)这个点被经过的次数。那么就有\(f[u]=\sum\limits_{w(u,v)}^n \frac{f[v]}{du[v]}\)。

对于第一个点一开始就被经过了一次,所以有\(f[1]=\sum\limits_{w(1,v)}\frac{f[v]}{du[v]}+1\)。因为最后一个点只会被经过一次而且与最后一个点向连的边,最后一个点无法对其产生贡献,所以最后一个点不要考虑。

列方程高斯消元解出来就行了。

code

/*
* @Author: wxyww
* @Date:   2020-04-27 20:14:39
* @Last Modified time: 2020-04-27 20:25:22
*/
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 510;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1; c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0'; c = getchar();
	}
	return x * f;
}
struct node {
	int v,nxt;
}e[N * N];
int head[N],ejs = 1;
void add(int u,int v) {
	e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;
}
double a[N][N];
int du[N];
void Guass(int n) {
	for(int i = 1;i <= n;++i) {
		int t = i;
		for(int j = i + 1;j <= n;++j)
			if(a[j][i] > a[t][i]) t = j;

		if(fabs(a[t][i]) < 1e-8) continue;
		swap(a[t],a[i]);

		double div = a[i][i];
		for(int j = i;j <= n + 1;++j) a[i][j] /= div;

		for(int j = 1;j <= n;++j) {
			if(j == i) continue;
			div = a[j][i];
			for(int k = i;k <= n + 1;++k)
				a[j][k] -= div * a[i][k]; 
		}
	}
}

double ans[N * N];
int tot;
int main() {
	int n = read(),m = read();
	for(int i = 1;i <= m;++i) {
		int u = read(),v = read();
		add(u,v);add(v,u);
		du[u]++;du[v]++;
	}

	a[1][n] = 1;

	for(int u = 1;u < n;++u) {
		a[u][u] = 1;
		for(int i = head[u];i;i = e[i].nxt ) {
			int v = e[i].v;
			if(v == n) continue;
			a[u][v] -= 1.0 / du[v];
		}
	}

	Guass(n - 1);

	for(int i = 2;i <= ejs;i += 2)
		ans[++tot] = a[e[i].v][n] / du[e[i].v] + a[e[i ^ 1].v][n] / du[e[i ^1].v];

	sort(ans + 1,ans + tot + 1);
	double anss = 0;
	for(int i = 1;i <= tot;++i)
		anss += ans[i] * (tot - i + 1);

	printf("%.3lf\n",anss);
	return 0;
}

标签:经过,一个点,bzoj3143,ll,HNOI2013,次数,游走,include,统计
来源: https://www.cnblogs.com/wxyww/p/bzoj3143.html