51nod 1301 集合异或和(DP)

本文介绍了一种使用动态规划解决特定数学问题的方法:当A小于B时,在二进制下找到所有可能的数对(A,B)的组合数量。通过枚举最高位的不同情况,设计状态dp[i][j][1/0]来表示前i个数,异或和为j,且B的第x位为1或0的情况数目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为当\(A<B\)时,会存在在二进制下的一位,满足这一位B的这一位是\(1\)\(A\)的这一位是\(0\).
我们枚举最大的这一位。设为\(x\)吧。
设计状态。\(dp[i][j][1/0]\)代表考虑了前i个数,异或和为j的情况下\(B\)的第\(x\)位为\(1\)\(0\)有多少种情况。
然后随便转移一下,再随便统计答案一下就好了。
如果不知道如何转移,就看代码吧。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
int n,m,mx,dp[2100][2100][2],ans;
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
int main(){
    n=read();m=read();
    mx=max(n,m);
    int now=0;
    for(int i=1;i<=mx;i<<=1){
        now++;
        memset(dp,0,sizeof(dp));
        dp[0][0][0]=1;
        for(int j=1;j<=mx;j++){
            for(int k=0;k<=2047;k++){
                if(j<=m){
                    dp[j][k][0]=(dp[j][k][0]+dp[j-1][k^j][0^((j&i)>>(now-1))])%mod;
                    dp[j][k][1]=(dp[j][k][1]+dp[j-1][k^j][1^((j&i)>>(now-1))])%mod;
                }
                if(j<=n){
                    dp[j][k][0]=(dp[j][k][0]+dp[j-1][k^j][0])%mod;
                    dp[j][k][1]=(dp[j][k][1]+dp[j-1][k^j][1])%mod;
                }
                dp[j][k][0]=(dp[j][k][0]+dp[j-1][k][0])%mod;
                dp[j][k][1]=(dp[j][k][1]+dp[j-1][k][1])%mod;
            }
        }
        for(int j=i;j<=min(i*2-1,2047);j++)ans=(ans+dp[mx][j][1])%mod;
    }
    printf("%d",ans);
    return 0;
}

转载于:https://www.cnblogs.com/Xu-daxia/p/10479755.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值