SRM 561

这题分两部分

第一部分是k个点组成的树边的总长

第二部分是每两*点的路径,做为了几个k集合的直径

第一部分:

就是求每个树边被几个集合用到,其实就是这个边去掉分成的两部分,*的个数分别是a,b,那么枚举 i = (1~  min(a,k))  方案数是C(a,i)*C(b,k-i)

第二部分:(感谢叉姐教我树直径性质)

枚举两点,然后找到这条路径的中心。所有点到这个中心的距离,小于等于那两个点的,都可以以那两个点作为直径。

关键是相等情况,去掉重复的(如果是同一边来的重复的还要特殊考虑。。。。)


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>

using namespace std;

class Orienteering {
public:
	double expectedLength(vector<string> , int);
};

double C[2000][2000];
void getC() {
	int i, j;
	for (i = 0; i < 2000; ++i) {
		C[i][0] = C[i][i] = 1;
		for (j = 1; j < i; ++j)
			C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
	}
}

vector<int> nt[2000];
int id[55][55], n, ff[2000];
int m;
int usid[2000], usn;
int usrid[305];
int dir[4][2] = { 0, 1, 1, 0, 0, -1, -1, 0 };

double ans;

double H[305];
void getH() {
	int i, j;
	for (i = 0; i <= usn; ++i) {
		H[i] = 0;
		for (j = 1; j < m && j <= i; ++j) {
			H[i] += C[i][j] * C[usn - i][m - j];
		}
	}
}
int cnt[2000];
void dfs(int i, int fa) {
	int j;
	if (ff[i])
		cnt[i] = 1;
	else
		cnt[i] = 0;
	for (j = 0; j < nt[i].size(); ++j) {
		if (nt[i][j] != fa) {
			dfs(nt[i][j], i);
			ans += H[cnt[nt[i][j]]];
			cnt[i] += cnt[nt[i][j]];
		}
	}
}
vector<pair<int, int> > len[2000];
vector<int> father[2000];
int now;
void getlen(int i, int fa, int l) {
	len[i].push_back(make_pair(l, now));
	father[i].push_back(fa);
	for (int j = 0; j < nt[i].size(); ++j)
		if (nt[i][j] != fa)
			getlen(nt[i][j], i, l + 1);
}

int path[2000];
int dis[305];
int where[305];
int from, to;
bool totarget(int i, int fa, int tar, int l) {
	//printf("%d\n", i);
	int j, k;
	path[l] = i;
	if (i == tar) {
		memset(dis, 0, sizeof(dis));
		if (l % 2) {
			k = path[l / 2];
			for (j = 0; j < len[k].size(); ++j) {
				dis[len[k][j].second] += len[k][j].first;
			}
			k = path[l / 2 + 1];
			for (j = 0; j < len[k].size(); ++j) {
				if (dis[len[k][j].second] < len[k][j].first)
					where[len[k][j].second] = path[l / 2];
				else
					where[len[k][j].second] = k;
				dis[len[k][j].second] += len[k][j].first;
			}
		} else {
			k = path[l / 2];
			for (j = 0; j < len[k].size(); ++j) {
				dis[len[k][j].second] += len[k][j].first;
				where[len[k][j].second] = father[k][j];
			}
		}
		k = 0;
		for (j = 0; j < usn; ++j) {
			if (dis[j] < dis[from])
				k++;
			else if (dis[j] == dis[from]) {
				if (where[j] == where[from] && j > from)
					k++;
				else if (where[j] == where[to] && j > to)
					k++;
				else if (j > to && j > from)
					k++;
			}
		}
		if (k >= m - 2)
			ans -= l * C[k][m - 2];
		return true;
	}
	for (int j = 0; j < nt[i].size(); ++j)
		if (nt[i][j] != fa) {
			if (totarget(nt[i][j], i, tar, l + 1))
				return true;
		}
	return false;
}
double Orienteering::expectedLength(vector<string> field, int K) {
	int i, j, k;
	n = 0;
	m = K;
	usn = 0;
	for (i = 0; i < field.size(); ++i) {
		for (j = 0; j < field[i].size(); ++j) {
			if (field[i][j] == '#')
				id[i][j] = -1;
			else {
				id[i][j] = n;
				if (field[i][j] == '*') {
					ff[n] = 1;
					usid[n] = usn;
					usrid[usn++] = n;
				} else
					ff[n] = 0;
				nt[n].clear();
				n++;
			}
		}
	}
	for (i = 0; i < field.size(); ++i) {
		for (j = 0; j < field[i].size(); ++j) {
			if (id[i][j] == -1)
				continue;
			for (k = 0; k < 4; ++k) {
				int ii = i + dir[k][0];
				int jj = j + dir[k][1];
				if (ii < 0 || jj < 0 || ii >= field.size() || jj
						>= field[i].size())
					continue;
				if (id[ii][jj] == -1)
					continue;
				nt[id[i][j]].push_back(id[ii][jj]);
			}
		}
	}
	getC();
	getH();
	ans = 0;
	dfs(0, -1);
	ans *= 2;
	for (i = 0; i < n; ++i) {
		len[i].clear();
		father[i].clear();
	}
	for (i = 0; i < usn; ++i) {
		now = i;
		getlen(usrid[i], -1, 0);
	}
	for (from = 0; from < usn; ++from) {
		for (to = from + 1; to < usn; ++to) {
			totarget(usrid[from], -1, usrid[to], 0);
		}
	}
	return ans / C[usn][m];
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值