题解 [AGC020F] Arcs on a Circle
\(\color{black}t\color{red}{ouist}\) 是世间真神!
发现坐标都是实数
猜测做法是计算所有弧两两之间的交的长度之和 \(\leqslant \sum l_i-c\) 的概率
然后看题解发现不是
发现本题有性质是弧长都是整数
将某条弧的左端点坐标写为 \(d_i+r_i, r_i\in[0, 1)\)
那么发现其右端点为 \(d_i+l_i+r_i\)
注意到这个 \(r_i\) 是不变的
又注意到因为左右端点 \(r_i\) 相同,我们其实可以离散化这个东西
但是不知道 \(r_i\) 的相对大小怎么离散化呢?
发现 \(n\) 很小,那么枚举 \(r_i\) 的相对大小关系
于是现在环被离散化成了 \(n*c\) 个整点
那么可以断环为链,在链上 DP 合法方案数
一个坑点是这里的断环为链不是乱断的,参考 tzc_wk 的题解
于是 DP 的话需要知道当前在哪个点,用了哪些弧,最远覆盖到那些点
求出合法方案数除以总方案数(相对大小关系总数*每个弧的起始点有 c 种选法)就可以了
复杂度 \(O(n!(nc)^22^n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, c;
double ans;
int l[10], p[10], id[10], f[310][310][1<<6], uni[55][10], tot;
signed main()
{
n=read(); c=read();
for (int i=1; i<=n; ++i) l[i]=read();
sort(l+1, l+n+1, [](int a, int b){return a>b;});
for (int i=1; i<=n; ++i) p[i]=i;
int lim=1<<n;
for (int i=1; i<=c; ++i)
for (int j=1; j<=n; ++j)
uni[i][j]=tot++;
do {
if (p[1]!=1) continue;
memset(f, 0, sizeof(f));
for (int i=1; i<=n; ++i) id[p[i]]=i;
f[uni[1][1]][uni[1][1]+l[1]*n][1]=1;
for (int i=1; i<=c; ++i) {
for (int j=1; j<=n; ++j) {
for (int k=uni[i][j]; k<=n*c; ++k) {
for (int s=1; s<lim; ++s) if (s&1) {
// if (f[uni[i][j]][k][s]) cout<<"f: "<<i<<' '<<j<<' '<<k<<' '<<bitset<6>(s)<<' '<<f[uni[i][j]][k][s]<<endl;
f[uni[i][j]+1][k][s]+=f[uni[i][j]][k][s];
if (!(s&(1<<id[j]-1))) f[uni[i][j]+1][max(k, min(n*c, uni[i][j]+l[id[j]]*n))][s|(1<<id[j]-1)]+=f[uni[i][j]][k][s];
}
}
}
}
ans+=f[n*c][n*c][lim-1];
} while (next_permutation(p+1, p+n+1));
for (int i=2; i<n; ++i) ans/=i;
for (int i=1; i<n; ++i) ans/=c;
printf("%.20lf\n", ans);
return 0;
}