[dp 记录] agc020F arcs on a circle
神题。yhx 的讲解 非常好、非常自然。
题意:
给定 和 段长度为 的弧,每条弧的起点在圆周上均匀随机一个位置,求所有弧的并集覆盖圆周的概率。
环上的问题并不好处理,因此寻求链是自然的。钦定一段弧的起点是一段弧的起点看着不错,这样子被覆盖的始终是一段前缀,然后呢?
现在需要用线段覆盖区间。为了提防一段弧同时覆盖前缀与后缀,把起点定位最长弧的左端点,每段弧就只会覆盖一个区间了。
弧是离散的情况是好做的。注意到,我们之所以需要离散这个条件,是因为我们需要比较两段弧的右端点的位置。
结论:两段弧是否相交仅与整数部分的数值与小数部分的大小关系有关。
因此全排列枚举大小关系即可。因为均匀分布,每种大小关系出现的可能性相等。由于连续分布,不用在意两数相等。
不妨让所有弧的坐标均是 的整数倍,则这个问题成为连续问题。
增量研究是好的。钦定一个考虑的顺序有助于递推。对于此题,从小到大考虑左端点可以保证只用记录最长能延申到的右端点。
每段弧的左端点只有 个可选位置,因此每个位置都有放或不放唯一一段对应弧的选择。有可能这段弧已经出现,多状压一维记录已经出现的弧的集合。
令 表示:只考虑左端点小于等于 的弧、用的弧的集合为 (下标即为人为赋予的相对顺序)、右端点连续延伸到 的方案数。转移平凡。
第一维被压掉了,因为同一层只会是小的 往大的 转移。两层间不用清空,对应左端点在此处的弧不被放入的情况。
#include <bits/stdc++.h>
using namespace std;
const int M = 55;
double f[33][305];
// f_{i, j, k} : 只考虑左端点 <= i 的线段,长度集合为 j,右端点延申至 k 的方案数
// i 一维被滚掉了,从小到大转移保证同层状态不会转移,因为有不放线段的转移,不用清空
int a[M], n, c;
int solve() {
memset(f, 0, sizeof(f)); f[0][a[n] * (n + 1)] = 1;
for (int i = 0; i < (n + 1) * c; i++) if (i % (n + 1)) {
int t = i % (n + 1) - 1, r = min((n + 1) * c, i + a[t] * (n + 1));
for (int j = 0; j < (1 << n); j++) if (!(j & (1 << t))) {
for (int k = i; k <= (n + 1) * c; k++)
f[j | (1 << t)][max(r, k)] += f[j][k];
}
}
return f[(1 << n) - 1][(n + 1) * c];
}
int main() {
scanf("%d %d", &n, &c); --n;
for (int i = 0; i <= n; i++) scanf("%d", &a[i]);
sort(a, a + n + 1);
int cnt = 0; double ans = 0;
do {
ans += solve(), ++cnt;
} while (next_permutation(a, a + n));
ans /= cnt;
for (int i = 1; i <= n; i++) ans /= c;
printf("%.11lf\n", ans);
}
作者:purplevine
出处:https://www.cnblogs.com/purplevine/p/17244248.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/17244248.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通