题意简述
n
个节点,每个节点的权值为
两个节点
i,j
之间的边权为
AixorAj
,求最小生成树的期望边权和。
答案对
258280327
取模。
数据范围
1≤n≤50
1≤m≤8
思路
看到xor考虑每一位单独考虑。
我们计算
h[k][i]
表示考虑
k
位,点数为
有一个性质:假设最高位为0的集合为
S1
,最高位为1的集合为
S2
,
S1
和
S2
之间仅有一条边相连。
我们这样就可以把
i
分成两个集合来做。
枚举
现在需要求出两个集合中间连边的边权。
f[k][i][j][S]
表示考虑
k
位,在两个大小为
枚举
i
集合和
逐位贪心,如果最高位有相同的肯定在最高位相同的之间连边,分类讨论一波。
g[k][i][j]
表示考虑
k
位,
最后把总和除以方案数
2nm
就行了,也可以直接DP期望。
复杂度
O(n42m)
比较卡
打表大法好。
代码
#include<cstdio>
using namespace std;
const int p=258280327;
int n,m;
int C[55][55],f[10][55][55][300],g[10][55][55],h[10][55],pw2[3010];
int quick_pow(int a,int x)
{
long long ret=1;
for (long long sum=a;x;sum=sum*sum%p,x>>=1)
if (x&1)
ret=ret*sum%p;
return (int)ret;
}
int main()
{
scanf("%d%d",&n,&m);
C[0][0]=1;
for (int i=1;i<=n;i++)
{
C[i][0]=1;
for (int j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
}
pw2[0]=1;
for (int i=1;i<=3000;i++)
pw2[i]=pw2[i-1]*2%p;
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++)
if (i==0||j==0)
{
f[0][i][j][0]=pw2[i+j];
f[0][i][j][1]=pw2[i+j];
}
else
{
f[0][i][j][0]=pw2[i+j];
f[0][i][j][1]=2;
}
for (int k=1;k<m;k++)
for (int i=0;i<=n;i++) for (int j=0;j<=n-i;j++)
{
//x,x
for (int ii=1;ii<i;ii++) for (int jj=1;jj<j;jj++)
for (int S=0;S<(1<<k);S++)
f[k][i][j][S]=(f[k][i][j][S]+1LL*C[i][ii]*C[j][jj]%p*f[k-1][ii][jj][S]%p*f[k-1][i-ii][j-jj][S])%p;
//0,x x,0
for (int ii=1;ii<i;ii++)
for (int S=0;S<(1<<k);S++)
f[k][i][j][S]=(f[k][i][j][S]+1LL*C[i][ii]*pw2[(i-ii)*k]%p*f[k-1][ii][j][S]*2)%p;
for (int jj=1;jj<j;jj++)
for (int S=0;S<(1<<k);S++)
f[k][i][j][S]=(f[k][i][j][S]+1LL*C[j][jj]*pw2[(j-jj)*k]%p*f[k-1][i][jj][S]*2)%p;
//0,all all,0
for (int S=(1<<k);S<(1<<k+1);S++)
f[k][i][j][S]=(f[k][i][j][S]+f[k-1][i][j][S-(1<<k)]*2)%p;
for (int S=0;S<(1<<k);S++)
f[k][i][j][S]=(f[k][i][j][S]+pw2[(i+j)*k]*2)%p;
//0,0 all,all
for (int S=0;S<(1<<k);S++)
f[k][i][j][S]=(f[k][i][j][S]+f[k-1][i][j][S]*2)%p;
}
for (int k=0;k<m;k++)
for (int i=1;i<=n;i++) for (int j=1;j<=n-i;j++)
for (int S=1;S<(1<<k+1);S++)
g[k][i][j]=(g[k][i][j]+f[k][i][j][S])%p;
for (int i=0;i<=n;i++)
for (int j=0;j<=i;j++)
h[0][i]=(h[0][i]+(i-j>0&&j>0)*C[i][j])%p;
for (int k=1;k<m;k++)
for (int i=0;i<=n;i++)
for (int j=0;j<=i;j++)
h[k][i]=(h[k][i]+(1LL*h[k-1][j]*pw2[(i-j)*k]+1LL*h[k-1][i-j]*pw2[j*k]+g[k-1][j][i-j]+(i-j>0&&j>0)*pw2[(i+1)*k])%p*C[i][j]%p)%p;
printf("%lld",1LL*h[m-1][n]*quick_pow(pw2[n*m],p-2)%p);
return 0;
}