题意
- 给定一个长为 n n n,宽为 m m m 的鱼缸,一个边长为 r r r 的正方形渔网。可以往鱼缸里放 k k k 条鱼,渔网不能超出鱼缸,问用渔网随机在鱼缸里捞鱼的最大期望是多少。
- 1 ≤ n , m ≤ 1 0 5 , 1 ≤ r ≤ min ( n , m ) , 1 ≤ k ≤ min ( n ⋅ m , 1 0 5 ) 1\le n,m\le 10^5,1\le r\le \min(n,m), 1\le k\le \min(n·m,10^{5}) 1≤n,m≤105,1≤r≤min(n,m),1≤k≤min(n⋅m,105)
思路
-
考虑每条鱼的贡献,将期望化为总答案除以方案数:
E = 每 条 鱼 被 覆 盖 次 数 之 和 正 方 形 数 量 E=\tfrac{每条鱼被覆盖次数之和}{正方形数量} E=正方形数量每条鱼被覆盖次数之和 -
发现从中间到四周,每条鱼的贡献递减,只需要从中间向四周用大根堆搜索入队 k k k 次,累加答案
-
每条鱼被覆盖次数:计算 x x x 方向上边界,下边界,相减加一即可, y y y 方向同理,然后相乘
代码
const int N = 2e5 + 5;
const int dx[] = {0, 0, -1, 1};
const int dy[] = {-1, 1, 0, 0};
int n, m, r, k;
ldb ans;
struct node {
int x, y;
ll val;
friend bool operator < (node a, node b) {
return a.val < b.val;
}
};
priority_queue<node> q;
struct point {
int x, y;
friend bool operator < (point a, point b) {
return a.x == b.x ? a.y < b.y : a.x < b.x;
}
};
map<point, bool> vis;
ll get(int x, int y) {
return (ll)(min(x + r - 1, n) - max(x, r) + 1) *
(min(y + r - 1, m) - max(y, r) + 1);
}
int main() {
cin >> n >> m >> r >> k;
int xx = (n + 1) / 2, yy = (m + 1) / 2;
q.push({xx, yy, get(xx, yy)});
vis[{xx, yy}] = true;
while(k--) {
node u = q.top(); q.pop();
ans += u.val;
int x = u.x, y = u.y;
rep(i, 0, 3) {
int nx = x + dx[i], ny = y + dy[i];
if(x < 1 || x > n || y < 1 || y > m || vis[{nx, ny}]) continue;
vis[{nx, ny}] = true;
q.push({nx, ny, get(nx, ny)});
}
}
printf("%.10Lf\n", ans / ((ll)(n - r + 1) * (m - r + 1)));
return 0;
}