51nod 1301 集合异或和

已知两个整数N与M,你需要构造两个整数集合X与Y,且需要满足以下要求:
(1)对所有的xi∈X,满足1<=xi<=N;对所有的yj∈Y,满足1<=yj<=M; (X与Y可以为空集)
(2)X∩Y=Φ;(但不要求集合X与Y的元素个数,只要两者没有交集即可)

不妨设构造后的集合X含有n个元素,而集合Y有m个元素,令 A=x1 xor x2 xor x3 xor ... xor xn,  B=y1 xor y2 xor y3 ... xor ym。其中,xor 为异或运算,并且 称A与B分别是集合X与集合Y的异或和。如果一个集合为空集,那它的异或和被认为是0。

求对于给定的N与M符合要求(1)、(2)且满足A<B的集合X、Y的不同构造方法个数,输出结果 mod 10^9 + 7。其中,两种构造方案被认为是不同的,当且仅当两种方案中的集合X或集合Y包含的元素集不完全相同。

例如:N=2,M=2,则符合条件的X与Y有如下四组:
X={}   and Y={1}
X={}   and Y={2}
X={}   and Y={1, 2}
X={1} and Y={2}
Input
一行两个整数N,M(1 <= N,M <= 2000)。
Output
一个整数,即方案个数取mod 1,000,000,007后的结果。
Input示例
2 2
Output示例
4
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

数位DP~

我们发现a和b不相等就是在从高往低的某一位i上两数a的i位为0而b的i位为1,那么我们可以枚举i。

我们用f[i][j][k]表示i位,a^b==j,b的该位为k的方案数,直接更新即可,那么ans就是所有j∈[2^i,2^(i+1))的f[max(n,m)][j][1]的和。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int mod=1e9+7;

int n,m,tot,f[2][2048][2],ans,kkz;

void add(int &a,int b)
{
	a=a+b>=mod ? a+b-mod:a+b;
}

int main()
{
	scanf("%d%d",&n,&m);
	tot=max(n,m);
	for(int i=0;(1<<i)<=tot;i++)
	{
		memset(f[kkz],0,sizeof(f[kkz]));
		f[kkz][0][0]=1;
		for(int j=1,exm;j<=tot;j++)
		{
			kkz^=1;
			memcpy(f[kkz],f[kkz^1],sizeof(f[kkz]));
			exm=(bool)(j&(1<<i));
			for(int k=0;k<2048;k++)
			{
				if(j<=n)
				{
					add(f[kkz][k][0],f[kkz^1][k^j][0]);
					add(f[kkz][k][1],f[kkz^1][k^j][1]);
				}
				if(j<=m)
				{
					add(f[kkz][k][0],f[kkz^1][k^j][exm]);
					add(f[kkz][k][1],f[kkz^1][k^j][exm^1]);
				}
			}
		}
		for(int j=(1<<i);j<(1<<(i+1));j++) add(ans,f[kkz][j][1]);
	}
	printf("%d\n",ans);
	return 0; 
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值