AGC020 F - Arcs on a Circle(神奇,概率 DP)
范围越小题越神,这是一道难度问号题哦。
复盘 \(\color{black}{\text{n}}\color{red}{\text{ealchen}}\) 神仙讲的题。
Description
你有一个长度为 \(C\) 的圆,你在上面画了 \(n\) 个弧,长度为 \(l_i\) 。
现在所有弧随机分布在圆上(坐标集为实数),求圆的每一个实点被至少一个弧覆盖的概率是多少?
\(2 \leq n \leq 6,\ 2 \leq C \leq 50,\ l_i < C\)
Analysis
这个范围有点让我们不知所措呵。
注意到坐标可以是实数,我们是肯定不能做到把所有实数拿来当候选坐标的。
然后有个 trick (?)大概就是对于每个整数 \(k\) ,分成 \(n\) 份,可以理解为把所有 \(n\) 个点坐标的小数部分拎出来离散化。
(因为弧长是整数所以两端小数部分相等所以才可以这样做)
所以我们直接把原来的长度 \(C\) 变成了 \(nC\) 个点。
还注意到这是一个圆,开始和结尾交合的地方比较麻烦,有注意到这个和顺序没有关系,所以我们可以直接先摁上去一个弧,这样就可以断链为环了。
剩下看起来就比较好处理了(
Sol1
其实局面已经比较明显了,我们可以通过枚举每个弧小数部分相对大小,然后用状压 \(\text{DP}\) 解决剩下的问题。
我们设 \(f_{i, S, j}\) 表示在长度不超过 \(i\) 的弧放置完毕,弧的选择状态集合为 \(S\) ,覆盖到的最远坐标在 \(j\) 的方案数。
转移从 \(i\) 到 \(i + 1\) 需要放置新的弧,枚举放置的起始点,更新一下最远距离就可以转移了。
大概就是:
算好了方案数,除以总方案数就行了: \((n - 1)! \cdot c ^ {n - 1}\) (主要我们一开始定了一个点)。
时间复杂度 \(O(2 ^ {n - 1} \cdot (nC) ^ 2 \cdot (n - 1)!)\) 。
Sol2
据说来自 \(\color{black}{\text{E}}\color{red}{\text{I}}\) ,反正很神仙就是了。
我们发现枚举小数部分相对大小有点浪费,考虑直接把小数部分排名也压进 \(f\) 里面,形如 \(f_{i, S, j, p}\) 表示在长度不超过 \(i\) 的弧放置完毕,弧的选择状态集合为 \(S\) ,覆盖到的最远坐标在 \(j\) ,最远弧小数部分大小排名 \(p\) 的方案数。
这样的话,更新最远距离的同时,要同时更新对应的排名,大概就是:
中括号就是插进来新的大小比自己小的小数排名的更新。
但是还有一个细节,我们需要的是整个都被覆盖,但是如果在同一个“1”内,前面的结束端点小数部分比后面开始端点的小数部分小,实际上是并没有覆盖完全的,需要排除掉。
大概就是 i - 1 的结束端点等于 i 的起始端点,且前者小数部分大小排名小于后者的,直接跳过。
时间复杂度精细实现可以做到 \(O(2 ^ {n - 1} \cdot C ^ 2 n ^ 3)\) 。
Sol3
插值做法?不会。但是理想时间复杂度似乎比 Sol2 高点。
Code
Sol1
/*
*/
#include
using namespace std;
#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define Check(a) freopen(a".in", "r", stdin), freopen(a".ans", "w", stdout);
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair pii;
#define fi first
#define se second
#define mp std::make_pair
const int mod = 1e9 + 7, i2 = 5e8 + 4;
template
inline int M(A x) {return x;}
template
inline int M(A x, B ... args) {return 1ll * x * M(args...) % mod;}
#define mi(x) (x >= mod) && (x -= mod)
#define ad(x) (x < 0) && (x += mod)
int n, c, m, le[7];
double f[306][36], ans, cnt;
inline int ksm(int a, int b) {
int tm = 1;
for (; b; b >>= 1, a = M(a, a)) b & 1 && (tm = M(a, tm));
return tm;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> c; m = 1 << n - 1;
for (int i = 0; i < n; ++i) std::cin >> le[i];
sort(le, le + n);
do {
for (int i = 0; i <= c * n; ++i) {
for (int s = 0; s < m; ++s) f[i][s] = 0;
}
f[le[n - 1] * n][0] = 1;//先把最后一个放上去
for (int i = 1; i <= c * n; ++i) {
if (!(i % n)) continue;
int p = i % n - 1;
for (int j = i; j <= c * n; ++j) {
for (int s = 0; s < m; ++s) ~s >> p & 1 && (
f[min(c * n, max(j, i + le[p] * n))][s | (1 << p)] += f[j][s]
);
}
}
ans += f[c * n][m - 1]; ++cnt;
} while (next_permutation(le, le + n - 1));
std::cout << fixed << setprecision(12) << ans / cnt / ksm(c, n - 1);
return 0;
}
Sol2
/*
*/
#include
using namespace std;
#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define Check(a) freopen(a".in", "r", stdin), freopen(a".ans", "w", stdout);
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair pii;
#define fi first
#define se second
#define mp std::make_pair
const int mod = 1e9 + 7, i2 = 5e8 + 4;
template
inline int M(A x) {return x;}
template
inline int M(A x, B ... args) {return 1ll * x * M(args...) % mod;}
#define mi(x) (x >= mod) && (x -= mod)
#define ad(x) (x < 0) && (x += mod)
int n, c, m, le[7], I[32];
ll f[32][52][7];
inline int ksm(int a, int b) {
int tm = 1;
for (; b; b >>= 1, a = M(a, a)) b & 1 && (tm = M(a, tm));
return tm;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> c; m = 1 << n - 1;
for (int i = 0; i < n; ++i) std::cin >> le[i];
sort(le, le + n);
f[0][le[n - 1]][0] = 1;
for (int i = 1; i < m; ++i) I[i] = I[i >> 1] + (i & 1);
for (int i = 0; i < c; ++i) {
for (int j = 1; j < n; ++j) {
for (int k = m - 1; ~k; --k) {
if (j > I[k] + 1) continue;
for (int l = i; l <= c; ++l) {
for (int p = 0; p <= I[k]; ++p) {
if (l == i && p < j) continue;
int po = p + (p >= j);
for (int q = 0; q < n - 1; ++q) if (~k >> q & 1) {
int len = std::min(i + le[q], c);
if (len > l || (len == l && j > po)) {
f[k | (1 << q)][len][j] += f[k][l][p];
}
else f[k | (1 << q)][l][po] += f[k][l][p];
}
}
}
}
}
}
double ans = 0;
for (int i = 0; i < n; ++i) ans += f[m - 1][c][i];
for (int i = 1; i < n; ++i) ans /= c * i;
std::cout << fixed << setprecision(12) << ans << "\n";
return 0;
}