51nod 1301 集合异或和——异或dp

Posted narh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51nod 1301 集合异或和——异或dp相关的知识,希望对你有一定的参考价值。

题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1301

好题!看了TJ才会。

因为是不可重集合,所以当然有前 i 个表示A和B都考虑的前 i 个,新加一个讨论放A、放B、不放。

A<B在异或上看就是有一位,它前面的A和B都一样,该位A是0、B是1。该位可以枚举。然后就能dp了。

注意边界细节……和标程对拍真愉快……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2005,M=4100,mod=1e9+7;
int n,m,mi,mj,lm,dp[2][M][2],ans,bin[25];
int calc(int a)
{
    int ret=0; while(a) a>>=1,ret++; return ret;
}
void ad(int &x,int y)
{
    x=(x+y>=mod?x+y-mod:x+y);
}
int main()
{
    scanf("%d%d",&n,&m);
    mi=max(n,m);  lm=calc(m);  int tmp=calc(mi);
    bin[1]=1;  for(int i=2;i<=tmp;i++) bin[i]=(bin[i-1]<<1);//tmp not lm!!!
    bin[tmp+1]=(bin[tmp]<<1);
    mj=bin[tmp+1]-1;
    for(int t=1;t<=lm;t++)
    {
        memset(dp[0],0,sizeof dp[0]);///not dp[0] if mj isn‘t gu ding
        dp[0][0][0]=1;
//        mj=1;
        for(int i=1,u,v;i<=mi;i++)
        {
            u=(i&1);v=!u;
//            if(i>=bin[mj])mj++;
//            for(int j=0;j<=bin[mj];j++)
            for(int j=0;j<=mj;j++)
            {
                dp[u][j][0]=dp[v][j][0];
                dp[u][j][1]=dp[v][j][1];
                if(i<=n)//A
                {
                    ad(dp[u][j][0],dp[v][j^i][0]);
                    ad(dp[u][j][1],dp[v][j^i][1]);
                }
                if(i<=m)//B
                {
                    bool d=(i&bin[t]);
                    ad(dp[u][j][0],dp[v][j^i][0^d]);
                    ad(dp[u][j][1],dp[v][j^i][1^d]);
                }
            }
        }
        int d=(mi&1);// mi not n!!!
        for(int i=bin[t];i<bin[t+1];i++) ad(ans,dp[d][i][1]);
    }
    printf("%d
",ans);
    return 0;
}

 

以上是关于51nod 1301 集合异或和——异或dp的主要内容,如果未能解决你的问题,请参考以下文章

51nod 1312 最大异或和(线性基)

51nod1312 最大异或和

两区间异或和最大

bzoj3687简单题 背包dp+STL-bitset

bzoj3687简单题(dp+bitset优化)

位运算 异或51nod区间xor