AtCoder Grand Contest 020 F: Arcs on a Circle

题目传送门:AGC020F


statement:

给定\(n\)条长度为\(L_i\)的线段(弧)和一个长度为\(C\)的圆,每条弧均匀随机地放置在圆上(允许实数),求每一个实点至少被一条弧覆盖的概率

\(n\leq 6\)

\(1\leq L_i<C\leq 50\)


solution:

首先考虑破坏成链,选取最大的一段弧将圆拆开。若第\(i\)段弧起始位置为\(X_i=p+q\),其中\(p=[x],q=\{x\}\)。任意两段弧的起始位置相同的概率为\(0\),因此只需考虑\(q\)的相对大小关系。于是在原长度为\(1\)的段上建立\(n-1\)个关键点,枚举\(q\)的相对大小关系直接DP即可。时间复杂度为\(\mathcal O((N-1)!2^{N-1}(CN)^2)\)

code

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 11;
const int MAXL = 311;
int L[MAXN];
int A[MAXN];
int rev[MAXN];
int dp[MAXL][MAXL][1 << 6];
int n, limL;
double ans;

int main(void) {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	
	cin >> n >> limL;
	for (int i = 1; i <= n; ++i)
		cin >> L[i], A[i] = i;
	sort(L + 1, L + 1 + n);
	
	do {
		memset(dp, 0, sizeof dp), dp[0][L[n] * n][0] = 1;
		for (int i = 1; i < n; ++i)
			rev[A[i]] = i;
		int LIM = n * limL;
		for (int i = 1; i <= LIM; ++i) {
			int id = i % n;
			id = rev[id];
			for (int j = max(i, L[n] * n); j <= LIM; ++j) {
				for (int s = 0; s < 1 << n - 1; ++s) {
					dp[i][j][s] += dp[i - 1][j][s];
					if ((!dp[i - 1][j][s]) || (id == 0)) 
						continue;
//					cerr << i << "  " << j << " " << s << '\n';
					if ((s >> id - 1) & 1)
						continue;
					dp[i][min(LIM, max(j, i + L[id] * n))][s | (1 << id - 1)] += dp[i - 1][j][s];
				}
			}
		}
		
		double cur = 1.0 * dp[LIM][LIM][(1 << n - 1) - 1];
		for (int i = 1;i < n; ++i)
			cur /= limL;
		ans += cur;
	} while (next_permutation(A + 1, A + n));
	for (int i = 2;i < n; ++i)
		ans /= i;
	printf("%.11lf\n", ans);
	return 0;
}
posted @ 2021-07-12 22:31  Beginner2670  阅读(50)  评论(0)    收藏  举报