图样
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的所有最小生成树的总代价和
则:
(读者自行理解不难)
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)
所以
然后我们来考虑怎么计算h[a][b][m][d]
我们来计算a,b集合中的每一层贡献
由于第m层已经确定了并且在f中已经把价值累加了
所以我们考虑把m-1层分成四个集合a0,a1,b0,b1
如果无法同时存在a0,b0或a1,b1,那么就必须在这里加一个贡献(因为现在必须选择两个第m-1位不同的数了)
所以此时(填表法)(由于第m-1位已经没有差别了,所以可以继续利用第m位分为两组)
当a0,b0同时存在或a1,b1同时存在,我们就需要枚举a0,b0集合的大小i,j
那么:
(注意,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);
}
浙公网安备 33010602011771号