題目
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);
}