[51nod1301]集合异或和

题目大意

已知两个整数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,m≤2000

一个重要的性质

这道题乍一看难以下手(这道题我卡了2个多月),直到有位大佬在讨论版说了思路才会做。

首先令A < B。把A和B都转成二进制,那么从高位开始找到A与B第一个不同的位,A的这一位一定是0,B的这一位一定是1。
然后把A和B异或起来,最高的为1的位,就是第一个不同的位。
知道了这个就很好做这道题了。

思路

首先枚举A与B第一个不同的位,假设是第x位,然后设f[i][j][k],表示已经分配好了前i个数,A xor B=j,并且B的第x位是k,可能的方案数。然后转移就很显然了。
统计答案时,把 2x+11i=2xf[max(n,m)][i][1] 加到答案里。这样枚举就能保证i高于x的位都相同,然后第x位不同,并且B的第x位为1,然后低位不管。
时间复杂度 O(n2logn)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=2005,mo=1e9+7,M=2048;

typedef long long LL;

int n,m,f[2][M][2],ans;

int main()
{
    scanf("%d%d",&n,&m);
    int N=max(n,m);
    for (int i=0;(1<<i)<=N;i++)
    {
        memset(f,0,sizeof(f));
        int p=0,q=1;
        f[p][0][0]=1;
        for (int j=1;j<=N;j++,p^=1,q^=1)
        {
            memcpy(f[q],f[p],sizeof(f[q]));
            int t=((j&(1<<i))>0);
            for (int k=0;k<M;k++)
            {
                if (j<=n)
                {
                    f[q][k][0]=(f[q][k][0]+f[p][k^j][0])%mo;
                    f[q][k][1]=(f[q][k][1]+f[p][k^j][1])%mo;
                }
                if (j<=m)
                {
                    f[q][k][0]=(f[q][k][0]+f[p][k^j][t])%mo;
                    f[q][k][1]=(f[q][k][1]+f[p][k^j][t^1])%mo;
                }
            }
        }
        for (int j=(1<<i);j<(1<<(i+1));j++) ans=(ans+f[p][j][1])%mo;
    }
    printf("%d\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值