整数拆分后状态最多38000+,由于已经连的边不能用,所以把还剩多少边压进状态,也就700+条边,然后记g[s][k]为s状态,省k条边的数学期望,直接列公式转移,n==40显然是要打表的,话说这道题大丰中学出过一次,也依稀记得见过,结果最后还是没想出来...
把表全部打出来大概2~3分钟
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define ll long long
const int mo=10007;
using namespace std;
int a[41],n,next[2000000],ss,d[100];
int sora[50000][41];
bool g[38000][781];
long double f[38000][781];
void origin()
{
for (int i=0;i<=mo;i++) next[i]=0;ss=mo;
memset(g,0,sizeof(g));
}
bool cmp(int a[41],int b[41])
{
if (a[0]!=b[0]) return 0;
for (int i=1;i<=a[0];i++)
if (a[i]!=b[i]) return 0;
return 1;
}
int hash(int a[41])
{
int h;
for (int i=1;i<=a[0];i++) h=(h+a[i]*d[i])%mo;
for (;next[h];) {
h=next[h];
if (cmp(sora[h-mo],a)) return h;
}
++ss,next[h]=ss,next[ss]=0;
for (int i=0;i<=n;i++) sora[ss-mo][i]=a[i];
return ss;
}
void dfs(int s,int a[41],int k)
{
//cout<<s<<' '<<k<<endl;
//for (int i=1;i<=a[0];i++) cout<<a[i]<<' ';cout<<endl;
g[s][k]=1;
if (a[0]==1) {
f[s][k]=0;
return ;
}
int b[41];
for (int i=0;i<=n;i++) b[i]=a[i];
long double p=0;
for (int i=1;i<=a[0];i++)
for (int j=1;j<i;j++)
p+=a[i]*a[j];
f[s][k]=0;
if (k>p) {
if (!g[s][k-1]) dfs(s,a,k-1);
f[s][k]+=(f[s][k-1]+1)*(k-p)/((long double)k);
for (int i=0;i<=n;i++) a[i]=b[i];
}
for (int i=1;i<=a[0];i++)
for (int j=1;j<i;j++) {
b[i]+=b[j],b[j]=mo;
sort(b+1,b+b[0]+1);b[0]--;
int ns=hash(b);
if (!g[ns-mo][k-1]) dfs(ns-mo,b,k-1);
for (int i=0;i<=n;i++) b[i]=a[i];
f[s][k]+=(f[ns-mo][k-1]+1)*(a[i]*a[j])/((long double)k);
}
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
d[0]=1;
for (int i=1;i<=50;i++) d[i]=(d[i-1]*131)%mo;
for (n=1;n<=40;n++) {
origin();
int a[41]={n};
for (int i=1;i<=n;i++) a[i]=1;
int s=hash(a);
dfs(s-mo,a,n*(n-1)/2);
//printf("b[%d]=%d;\n",n,(int)f[s-mo][n*(n-1)/2]);
cout<<"b["<<n<<"]="<<(int)f[s-mo][n*(n-1)/2]<<';'<<endl;
}
return 0;
}