这题分两部分
第一部分是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];
}