文件详细信息

下载本文件

本文件的大小为 2029 字节。

#include<algorithm>
#include<cstdio>
typedef long long ll;
const int P=1000000007;
inline int qmod(const int x){
	return x+((x>>31)&P);
}
inline void add(int&a,const ll b){
	a=(a+b)%P;
}
ll n,m,t;
int k,L,f[51][1600][51],C[51][51],g[51][51][51],lim,tmp;
int main(){
	scanf("%lld%lld%d",&n,&m,&k);//程序中的 $n$、$m$、$k$ 对应者题目中的 $A$、$B$、$n$。
	t=std::max(n,m);
	if(!t){
		puts("1");
		return 0;
	}
	L=63-__builtin_clzll(t);//注意:$t=0$ 时,__builtin_clzll(t) 是未定义行为,小心踩坑!
	f[L+1][n>>L&1][k]=1;
	for(int i=0;i<=k;i++){
		C[i][0]=1;
		for(int j=1;j<=i;j++)C[i][j]=qmod(C[i-1][j-1]+C[i-1][j]-P);
	}
	for(int i=0;i<=k;i++)
		for(int j=0;j<=k;j++)
			for(int p=0;p<=j;p++)
				g[i][j][p]=(ll)C[i][p]*C[k-i][j-p]%P;
	lim=(k>>1)*((k+1)>>1)*2;
	//$i$ 为当前考虑的位,要从高到低考虑,因为某一位的异或和加起来之后只会影响更高的位,从高到低枚举就满足了无后效性。
	for(int i=L;i>=0;i--)
		if(m>>i&1)
			for(int j=0;j<=lim;j++)//原本需要 $j$ 个第 $i+1$ 位,更低的位不用管,因为这里管不到。
				for(int p=0;p<=k;p++){//原本有 $p$ 个数顶着上界。
					if(f[i+1][j][p])
						for(int x=0;x<=k;x++)//第 $i$ 位有 $x$ 个 $0$。
							if(j>=x*(k-x)&&(tmp=(j-x*(k-x))<<1|(i?n>>(i-1)&1:0))<=lim)
								//在当前的 $x$ 个顶上界的数中,选 $y$ 个让它的第 $i$ 位变为 $0$,就不再顶上界了。
								for(int y=std::max(x-k+p,0);y<=p&&y<=x;y++)
									//转移的系数为 ${p \choose y} \times {{k-y} \choose {x-y}}$,分别是顶上界和不顶上界的数分别进行选择的方案数。
									add(f[i][tmp][p-y],(ll)g[p][x][y]*f[i+1][j][p]);
				}
		else
			for(int j=0;j<=lim;j++)
				for(int p=0;p<=k;p++)
					if(f[i+1][j][p])
						for(int x=p;x<=k;x++)
							if(j>=x*(k-x)&&(tmp=(j-x*(k-x))<<1|(i?n>>(i-1)&1:0))<=lim)
								add(f[i][tmp][p],(ll)C[k-p][k-x]*f[i+1][j][p]);
	ll ans=0;
	for(int i=0;i<=k;i++)ans+=f[0][0][i];
	printf("%d\n",int(ans%P));
	return 0;
}