异或树[jzoj6707]

题意

problem

题解

显然这是一道DP题。看起来似乎只能从高位到低位考虑。

假设有些点的最高位为 $0$,剩下的点最高位为 $1$,那么这些点形成了两个连通块。

这两个连通块内部先做一次最小生成树,然后之间需要连一条边权最小的边链接这两个连通块。

现在我们先来算,假设你考虑到某一位 $i$(从 $0$ 开始),有两块的大小是 $k,l$,这时候中间这条边的权值的期望*方案数是多少,记这个数为 $g_{i,k,l}$。

然而这个 $g$ 是无法直接算的。考虑转换最小值,有一个经典的套路,要算一个随机变量 $x$ 的期望,那么可以通过以下转换:

$$E(x)=\sum_{i=0} i[P(x=i)]=\sum_{i=1}[P(x\geq i)]$$

我们转换成求这条权值最小的边大于等于某个数 $j(j< 2^i)$ 的方案数,也就是所以跨过两个块的边都大于等于这个 $j$ 的方案数。设这个方案数为 $f_{i,j,k,l}$。

现在只需要考虑 $f$ 如何计算。

进行大力分类讨论:

  1. $k=0$ 或 $l=0$

    这时候为了方便其他 $f$ 的计算,可以令 $f_{i,j,k,l}$ 直接表示为所有的方案数,即 $2^{(i+1)(k+l)}$。

  2. $j$ 的第 $i$ 位为 $1$

    这时候 $k,l$ 都大于 $0$ 了,那么这两个块的最高位异或后必须为 $1$,也就是一个块的最高位全是 $0$ ,令一个的最高位全是 $1$,这样的方案数有两种,然后还需要看 $j$ 除去第 $i$ 位后的结果,也就是 $f_{i-1,j\oplus 2^i,k,l}$。

  3. $j$ 的第 $i$ 位为 $0$

    此时,这两个块的最高位就没有什么限制了,也就是块内的最高位可以不相同。那么,就需要分别枚举两个块有多少个最高位为 $0$ 的,设为 $u,v$,一共有 $\binom{k}{u}\times \binom{l}{v}$ 种选法,对于 $j$ 的限制,显然不需要考虑不同块且最高位不同的点,方案数为 $f_{i-1,j,u,v}\times f_{i-1,j,k-u,l-v}$。

然后大力转移就好了。需要注意 $i=0$ 时的边界。

现在你已经求出了所有的 $g_{i,k,l}$。

最后需要将这些 $g_{i,k,l}$ 转换成答案。

我们可以再来一个DP,设 $h_{i,j}$ 表示考虑到第 $i$ 位(从 $0$ 开始),一共 $j$ 个点时的答案之和。

然后还是按照最高位分两块,枚举其中一块的点数 $k$,则有:

$$h_{i,j}=\sum_{k=0}^j\binom{j}{k}\left (2^{i(j-k)}h_{i-1,k}+2^{ik}h_{i-1,j-k}+[k>0,j>k](g_{i-1,k,j-k}+2^i\times 2^{ij})\right )$$

最后面的 $[k>0,j>k]$ 是需要保证两个块都有点,才能有一条跨过两块的边。

时间复杂度 $O(mn^42^m)$,有超级多个 $\frac{1}{2}$ 的常数,因此卡卡常,减小一下取模的次数就过了。

注意 $n=1$ 的特判。

程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <bitset>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
ll mod;
inline ll Add(ll x,ll y){x+=y; return (x<mod)?x:x-mod;}
inline ll Dec(ll x,ll y){x-=y; return (x<0)?x+mod:x;}
inline ll Mul(ll x,ll y){return x*y%mod;}
inline ll Pow(ll x,ll y)
{
y%=(mod-1);ll ans=1;for(;y;y>>=1,x=x*x%mod)if(y&1) ans=ans*x%mod;
return ans;
}
const int N=53;
int n,m;
ll p2[N*N],C[N][N],f[9][260][N][N],h[9][N],g[9][N][N];
int main()
{
FO(xor);
cin>>n>>m>>mod;
if(n==1) return printf("0")&0;
p2[0]=1;
fo(i,1,N*N-1) p2[i]=(p2[i-1]*2)%mod;
fo(i,0,n) C[i][0]=C[i][i]=1;
fo(i,1,n)
fo(j,1,i-1)
C[i][j]=Add(C[i-1][j],C[i-1][j-1]);
fo(i,1,n)
fo(j,1,n-i)
f[0][0][i][j]=p2[i+j],f[0][1][i][j]=2;
fo(i,1,m)
ff(j,0,1<<i)
fo(k,0,n)
f[i-1][j][k][0]=f[i-1][j][0][k]=p2[i*k];
ll tmp;
fo(i,1,m-1)
ff(j,0,1<<(i+1))
fo(k,1,n)
fo(l,1,n-k)
if(j&(1<<i))
f[i][j][k][l]=f[i-1][j^(1<<i)][k][l]*2%mod;
else
{
fo(u,0,k)
{
tmp=0;
fo(v,0,l) tmp=Add(tmp,C[l][v]*f[i-1][j][u][v]%mod*f[i-1][j][k-u][l-v]%mod);
f[i][j][k][l]+=tmp*C[k][u]%mod;
}
f[i][j][k][l]%=mod;
}
fo(i,0,m-1)
fo(k,1,n)
fo(l,1,n-k)
{
ff(j,1,p2[i+1])
g[i][k][l]+=f[i][j][k][l];
g[i][k][l]%=mod;
}
fo(i,1,n) h[0][i]=Dec(p2[i],2);
fo(i,1,m-1)
fo(j,1,n)
fo(k,0,j)
h[i][j]=Add(h[i][j],Mul(C[j][k],Add(Add(h[i-1][k]*p2[i*(j-k)]%mod,h[i-1][j-k]*p2[i*k]%mod),(k>0&&j>k)*Add(g[i-1][k][j-k],p2[i*j]*p2[i]%mod))));
printf("%lld",h[m-1][n]);
return 0;
}