2019牛客多校赛 第七场 H Pair(数位dp)

2019牛客多校赛 第七场 H Pair(数位dp)_第1张图片

2019牛客多校赛 第七场 H Pair(数位dp)_第2张图片

 

 

大致题意:告诉你范围A和B,让你求在两个范围内,有多少对数字可以使得二者按位与大于C或者异或小于C。

一个比较常规的数位dp,然而比赛的时候由于自己复杂度计算错误,还写了好久的优化,最后发现不优化也能过。

我们令dp[len][x][y][lim1][lim2]表示在二进制下,当前长度为len的时候,第一个条件的状态为x,第二个条件状态为j,第一个数字的限制情况为lim1,第二个为lim2时候的方案数。这里,我们条件总共有3个状态:0表示可能满足,1表示已经满足,2表示已经不满足。这是因为在二进制下的大于与小于关系,如果从高位往地位走,在某一位确定大小之后,后面就不会改变结果,而数位dp的过程本身就是高位到低位走的。

然后,转移的话,直接枚举两个数字在第len位的取值0或者1,然后根据当前两个条件的状态判断两个数字的取值是否合法,以及条件状态的变化。最后,结束条件是长度为0的时候,如果两个条件之一满足,那么当前方案合法。

最后说一下可有可无的优化,如果到某一个长度的时候,发现已经有一个条件满足了,那么我们可以不用继续算下去,根据两个数字的限制情况可以直接计算方案数。另外,如果某个时刻,两个条件都已经不满足,那么直接返回方案数0。

最后的最后,由于数字取值不能是0,所以要减去所有包含0的答案。具体见代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define N 2010
#define INF 0x3f3f3f3f3f3f3f3fll
#define eps 1e-5
#define pi 3.141592653589793
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define ls (node<<1)
#define rs (node<<1|1)
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<"      :   "<num3[len]||c2==2)) continue;
            if (!c1) if (xnum3[len]) nc1=1; else nc1=0;
            if (!c2) if (y>num3[len]) nc2=2; else if (y>=1;
    while(B) num2[++t2]=B&1,B>>=1;
    while(C) num3[++t3]=C&1,C>>=1;
    int t=max(t1,max(t2,t3));
    for(int i=t;i;i--)
    {
        n1[i]=n2[i]=0;
        for(int j=i;j;j--)
        {
            n1[i]=n1[i]<<1|num1[j];
            n2[i]=n2[i]<<1|num2[j];
        }
    }
    return dfs(t,0,0,1,1);
}

void init()
{
    pw[0]=1;
    for(int i=1;i<31;i++)
        pw[i]=pw[i-1]*2;
}

int main(){
    int T;
    sc(T);
    init();
    while(T--){
        memset(dp,-1,sizeof(dp));
        int A,B,C; sccc(A,B,C);
        printf("%lld\n",cal(A,B,C)-min(A,C-1)-min(B,C-1)-1);
    }
    return 0;
}

 

你可能感兴趣的:(---------Online,Judge--------,牛客,2019牛客多校赛,数位dp)