#4713. 方程

题目描述
题解

考虑最高位 k k k ,如果 m i m_i mi k k k 位为 1 1 1 ,且 x i x_i xi k k k 位为 0 0 0 的话,那其他的 x x x 可以取任意值,因为 x i x_i xi 可以取到 [ 0 , 2 k ) [0,2^k) [0,2k) 的任意一个数,所以可以调整一下,据此我们可以列出dp: f i , j f_{i,j} fi,j 表示前 i i i 个数,有 j j j 个数第 k k k 位为 1 1 1 ,可以得到方程的解的组数,那如果 m i m_i mi 1 1 1 的话可以列出转移式子 f i , j = f i − 1 , j × 2 k + f i − 1 , j − 1 × ( m i − 2 k + 1 ) f_{i,j}=f_{i-1,j} \times 2^k+f_{i-1,j-1} \times (m_i-2^k+1) fi,j=fi1,j×2k+fi1,j1×(mi2k+1) ,最后要记得除以 2 k 2^k 2k 因为其他数确定了,这个数也就确定了,所以只有 × 1 \times 1 ×1 的贡献,然后继续递归即可

效率: O ( 30 T n 2 ) O(30Tn^2) O(30Tn2)

代码
#include <bits/stdc++.h>
using namespace std;
const int P=1e9+7;
int n,m,a[55],f[55][55],w[55];
int solve(int x){
	if (x<0) return 1;
	f[0][0]=1;int u=0,v=0;
	for (int i=1;i<=n;i++)
		if (a[i]&(1<<x)){
			u++;f[u][0]=1ll*f[u-1][0]*(1<<x)%P;
			for (int j=1;j<=u;j++)
				(f[u][j]+=(1ll*f[u-1][j]*(1<<x)%P+1ll*f[u-1][j-1]*((a[i]&((1<<x)-1))+1)%P)%P)%=P;
		}
		else for (int j=0;j<=u;j++) f[u][j]=1ll*f[u][j]*((a[i]&((1<<x)-1))+1)%P;
	for (int i=0;i<u;i++)
		if ((i&1)==((m>>x)&1))
			(v+=1ll*f[u][i]*w[x]%P)%=P;
	for (int i=0;i<=u;i++)
		for (int j=0;j<=u;j++) f[i][j]=0;
	if ((u&1)==((m>>x)&1))
		return (v+solve(x-1))%P;
	return v;
}
int main(){
	w[0]=1;w[1]=(P+1)>>1;
	for (int i=2;i<55;i++)
		w[i]=1ll*w[i-1]*w[1]%P;
	while(~scanf("%d%d",&n,&m)){
		for (int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		printf("%d\n",solve(30));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值