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\) 需要放置新的弧,枚举放置的起始点,更新一下最远距离就可以转移了。

大概就是:

\[f_{i, S, j} \rightarrow f_{i + 1, S \cup \{x\}, \max(j,\ k + len \cdot n)} \]

算好了方案数,除以总方案数就行了: \((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\) 的方案数。

这样的话,更新最远距离的同时,要同时更新对应的排名,大概就是:

\[f_{i, S, j, p} \rightarrow f_{i + 1, S \cup \{x\}, \max(\{j,\ p + [q \leq p] \},\ \{k + len \cdot n,\ q\})} \]

中括号就是插进来新的大小比自己小的小数排名的更新。

但是还有一个细节,我们需要的是整个都被覆盖,但是如果在同一个“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;
}

posted @ 2022-08-08 16:47  Illusory_dimes  阅读(29)  评论(0编辑  收藏  举报