题目
Description
Input
Output
Sample Input
样例输入1:
2 2 998244353
样例输入2:
10 3 998244353
Sample Output
样例输出1:
24
样例输出2:
579443574
Data Constraint
思路
考虑知道点权怎么求
建出trie,对于trie的每个节点,如果它既有0儿子,又有1儿子,那么这两棵子树分别联通后,要找一条最小的边把它们连起来。
于是我们可以枚举这个节点的深度,再枚举它的左子树和右子树的大小,问题转换为:
有x和y个k位二进制数,求它们之间的异或最小值。
设f[x][y][z][u]为有x和y个k位二进制数,最小值≥u的方案数。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=57,M=1<<9;
ll mod;
ll power(ll x,ll t)
{
ll b=1;
while(t)
{
if(t&1) b=b*x%mod;
x=x*x%mod; t>>=1;
}
return b;
}
int n,m,_2[10];
ll c[M][M],p[N][N][10][M],b[N];
void init(int n)
{
for(int i=0; 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])%mod;
}
}
int main()
{
freopen("xor.in","r",stdin); freopen("xor.out","w",stdout);
scanf("%d%d%d",&n,&m,&mod);
init(1<<8);
_2[0]=1;
for(int i=1; i<=8; i++) _2[i]=_2[i-1]*2;
for(int i=0; i<=n; i++) for(int j=0; j<=n; j++) for(int k=0; k<=m; k++) if(!i||!j||!k)
{
ll s=1;
for(int w=1; w<=i+j; w++) s=s*_2[k]%mod;
for(int w=0; w<_2[k]; w++) p[i][j][k][w]=s;
}
for(int i=1; i<=n; i++) for(int j=1; j<=n-i; j++) for(int k=1; k<=m; k++)
{
for(int I=0; I<=i; I++) for(int J=0; J<=j; J++)
{
int t=_2[k-1];
if((I&&J)||((i-I)&&(j-J))) t=0;
ll yjy=c[i][I]*c[j][J]%mod;
for(int w=0; w<_2[k-1]; w++)
{
if(t==0) p[i][j][k][w+t]=(p[i][j][k][w+t]+p[I][J][k-1][w]*p[i-I][j-J][k-1][w]%mod*yjy)%mod;
else p[i][j][k][w+t]=(p[i][j][k][w+t]+p[I][j-J][k-1][w]*p[i-I][J][k-1][w]%mod*yjy)%mod;
}
}
ll s=p[i][j][k][_2[k-1]];
for(int w=0; w<_2[k-1]; w++) p[i][j][k][w]=(p[i][j][k][w]+s)%mod;
}
ll ans=0;
for(int i=1; i<=m; i++)
{
b[0]=1;
b[1]=_2[m]-_2[i];
for(int j=2; j<=n; j++) b[j]=b[j-1]*b[1]%mod;
for(int j=1; j<=n; j++) for(int k=1; k<=n-j; k++)
{
ll yjy=c[n][j]*c[n-j][k]%mod*_2[m-i]%mod*b[n-j-k]%mod;
ll yjy2=1;
for(int w=1; w<=j+k; w++) yjy2=yjy2*_2[i-1]%mod;
ans=(ans+yjy*_2[i-1]%mod*yjy2)%mod;
ll s=0;
for(int w=1; w<_2[i-1]; w++) s=(s+p[j][k][i-1][w])%mod;
ans=(ans+yjy*s)%mod;
}
}
printf("%lld\n",ans);
}