BZOJ 2685: Sgu385 highlander
Sol
期望DP.
\(f[i][j][k]\) 表示已经确定了 \(i\) 个点, 最大环大小为 \(j\) ,个数为 \(k\) 的方案数.
转移非常复杂,因为细节特别多.
\(f[i][j][1]=\frac{A_{j}^{n}}{j},i=j,k=1\)
\(f[i][j][1]=\frac{A_{j}^{n-i+j}}{j} \sum_{l=1}^{j} g[i][j],i>j,k=1\) 这个地方的 \(g[i][j]\) 我写的跟论文里的不太一样...不知道是论文里写错了还是我理解错了.
\(f[i][j][k]=f[i-j][j][k-1]\frac{A_{j}^{n-i+j}}{jk},i>j,1<k*j<=i\) 因为要考虑几个环的顺序,所以要除去一个 \(k\) 的排列.
其中 \(g[i][j]=\sum_{l=2}^{\frac{i}{j}} f[i][j][l] \)
\(n<=100\) 没人打表?打表Rank1系列.
Update:因为期望总是小于 \(100\) ,而double类型以对数方式储存,所以可以保证答案的正确性.
Code
/************************************************************** Problem: 2685 User: BeiYu Language: C++ Result: Accepted Time:392 ms Memory:10508 kb ****************************************************************/ #include<cstdio> #include<iostream> using namespace std; const int N = 105; int n; double fac[N],A[N][N]; double f[N][N][N],g[N][N],d[N]; double ans; int main(){ // freopen("in.in","r",stdin); scanf("%d",&n); fac[0]=1;for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) A[i][j]=fac[i]/fac[i-j]; d[1]=0,d[2]=1;for(int i=3;i<=n;i++) d[i]=(i-1)*(d[i-1]+d[i-2]); for(int i=1;i<=n;i++){ for(int j=2;j<=i;j++){ if(i==j) f[i][j][1]=A[n][j]/j; else{ for(int l=2;l<j;l++) f[i][j][1]+=g[i-j][l]; f[i][j][1]*=A[n-i+j][j]/j; } for(int k=2;k*j<=i;k++) f[i][j][k]=f[i-j][j][k-1]*A[n-i+j][j]/j/k; } for(int j=2;j<=i;j++) for(int k=1;k*j<=i;k++) g[i][j]+=f[i][j][k]; } for(int j=2;j<=n;j++) for(int k=1;k*j<=n;k++) ans+=j*k*f[n][j][k]; ans/=d[n]; printf("%.14lf\n",ans); return 0; }