图样

Time Limit: 10 Sec  Memory Limit: 128 MB

Description

小火车励志成为一名辣鸡出题人,但是要成为一名辣鸡出题人,代码必须跑得比谁都快,这样就能把他们都卡常数

了!为了锻炼自己,他找到了一位长者--乐滋滋,乐滋滋说:"你啊,tooyoung!西方的哪一个国家我没有去过?"

小火车坐在高高的骨灰旁边,听长者讲那西方的事情。西方有n个国家,长者决定向西方的每个国家普及人生经验

,但首先要让他们互通火车,第i个国家有一个权值Ai,修建连接第i个国家到第j个国家的铁路,需要付出Ai xor 

Aj(xor表示按位异或)的代价,长者希望代价总和尽量小(也就是选择一个最小生成树)。但是在长者以前,没

人去过西方,所以不知道每个国家的权值。但是我们知道每个国家的权值都是一个在0到2^m-1之间的随机整数,长

者希望知道他所需要付出的代价的期望。当然,答案是一个有理分数,为了避免精度误差长者需要你输出这个分数

在模258280327(2*317+1,一个质数)意义下的值(如果不存在则输出-1)。

 

Input

一行两个正整数,分别表示n和m。

n<=50,m<=8

 

Output

一行一个正整数表示答案。

 

Sample Input

2 2

Sample Output

129140165

 

 

 

 

 

 

题解

考试的时候直接写了暴力,然而把prim打成dijkstra了。。。。竟然过了样例2 2

来考虑正解

设f[n][m]表示n个点,点权在0~2^m-1的所有最小生成树的总代价和

f[n][m]=\sum_{i=0}^{n}f[i][m-1]*(2^{m-1})^{n-i}+f[n-i][m]*(2^{m-1})^{i}+g[i][n-i][m-1]+2^{m-1}*(2^{m-1})^n(读者自行理解不难)

g[a][b][m]表示第m位为0有a个数,为1的有b个数,所有方案的最小边(连通a,b的最小权值边)的代价和。

发现g[a][b][m]如果直接枚举最小边权值来计算并不好算

 

我们可以换一个求和顺序

(cnt[x][y][i]表示连通集合x,y的最小边权恰好为i的方案数)

(h[x][y][i]表示连通集合x,y的最小边权大于等于i的方案数)

(这两个数组的定义都是点权在0~2^m-1的范围,所以还要自带一维m)

g[a][b][m]

=cnt[x][y][1]+2*cnt[x][y][2]+3*cnt[x][y][3]+...+(2^m-1)*cnt[x][y][2^m-1]

=h[x][y][1]+h[x][y][2]+...+h[x][y][2^m-1]

所以

g[a][b][m]=\sum_{i=0}^{2^m-1}h[a][b][m][i]

 

然后我们来考虑怎么计算h[a][b][m][d]

我们来计算a,b集合中的每一层贡献

由于第m层已经确定了并且在f中已经把价值累加了

所以我们考虑把m-1层分成四个集合a0,a1,b0,b1

如果无法同时存在a0,b0或a1,b1,那么就必须在这里加一个贡献(因为现在必须选择两个第m-1位不同的数了)

所以此时h[a][b][m][d]+=h[a][b][m-1][d-2^{m-1}](填表法)(由于第m-1位已经没有差别了,所以可以继续利用第m位分为两组)

当a0,b0同时存在或a1,b1同时存在,我们就需要枚举a0,b0集合的大小i,j

那么:

h[a][b][m][d]=\sum_{i=0}^{a}\sum_{j=0}^bC[a][i]*C[b][j]*h[i][j][m-1][d]*h[a-i][b-j][m-1][d]

(注意,i=0时j不能等于b,j=0时i不能等于a,否则就是第一种情况)

 

然后就可以做了,最后再除一个总方案数

利用记忆化搜索的方式来实现会更容易

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 55
#define M 8
const int mod=258280327;
int f[N][M+1],g[N][N][M+1],h[N][N][M+1][(1<<M)+5];
int C[N][N],pw[N*N];
int ksm(int x,int y)
{
	int ans=1;
	while(y){
		if(y&1)ans=1ll*ans*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ans;
}
int solveH(int a,int b,int m,int d)
{
	if(a>b)swap(a,b);
	if(a==0||d<=0)return pw[(a+b)*m];
	if(d>=(1<<m))return 0;
	int &sum=h[a][b][m][d];
	if(sum!=-1)return sum;
	sum=0;
	for(int i=0;i<=a;i++)
		for(int j=0;j<=b;j++)
			if((i==0&&j==b)||(j==0&&i==a))
				sum=(sum+solveH(a,b,m-1,d-(1<<m-1)))%mod;
			else
				sum=(1ll*sum+1ll*solveH(i,j,m-1,d)*solveH(a-i,b-j,m-1,d)%mod*C[a][i]%mod*C[b][j]%mod)%mod;
	return sum;
}
int solveG(int a,int b,int m)
{
	if(m<=0)return 0;
	if(a>b)swap(a,b);
	int &sum=g[a][b][m];
	if(sum!=-1)return sum;
	sum=0;
	for(int i=1;i<(1<<m);i++)
		sum=(sum+solveH(a,b,m,i))%mod;
	return sum;
}
int solveF(int n,int m)
{
	if(n<=1||m<=0)return 0;
	int &sum=f[n][m];
	if(sum!=-1)return sum;
	sum=2*solveF(n,m-1)%mod;
	for(int i=1;i<n;i++)
		sum=(1ll*sum+1ll*(1ll*solveF(i,m-1)*pw[(m-1)*(n-i)]+1ll*solveF(n-i,m-1)*pw[(m-1)*i]+1ll*solveG(i,n-i,m-1)+1ll*pw[(m-1)*(n+1)])%mod*C[n][i])%mod;
	return sum;
}
int main()
{
	//freopen("young.in","r",stdin);
	//freopen("young.out","w",stdout);
	int n,m,i,j;
	scanf("%d%d",&n,&m);
	memset(f,-1,sizeof(f));memset(g,-1,sizeof(g));memset(h,-1,sizeof(h));
	pw[0]=1;for(i=1;i<=2500;i++)pw[i]=2ll*pw[i-1]%mod;
	for(i=1;i<=50;i++){
		C[i][0]=C[i][i]=1;
		for(j=1;j<i;j++)
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	printf("%d",1ll*solveF(n,m)*ksm(pw[n*m],mod-2)%mod);
}