[数位dp] Pair

输入正整数A,B,C,统计满足1≤x≤A,1≤y≤B且至少满足下列条件之一:
①x and y > C
②x xor y < C
的对儿(x,y)有多少个.
数据范围:1≤A,B,C≤109.

分析

109的二进制数至多30位,所以我们在二进制下做数位dp.

至少满足①或②的对立面是既不满足①也不满足②,即(and条件)且(xor条件).我们转而统计这种对儿有多少个,因为这样做比较方便.

在设计dp之前,我们先思考一些简单的问题.

首先,我们考虑x和y的最高位,因为是最高位,所以仅就这1位而言它必然要同时满足and条件和xor条件,我们定义为"边界and条件",为"严格and条件",xor条件也做相同定义.那么最高位有这么4种可能:

①严格and,严格xor
那么后一位(次高位)就可以无视and和xor这两个条件了,因为无论如何都会满足的.
②边界and,严格xor
那么后一位可以无视xor这个条件了
③严格and,边界xor
那么后一位可以无视and这个条件了
④边界and,边界xor
那么nothing will happen

所以我们设计dp[i][and][xor]表示:考虑第i位,是/否满足and条件,是/否满足xor条件的方案数.如果最高位是30位,那么最终的答案是dp[30][1][1],后两个参数均为1表示满足and和xor条件.

但这样还有问题,就是我们没有考虑A,B对x,y的限制.
同样,从最高位的情况开始思考,对于x,y的最高位分别与A,B的关系我们有4种情况:

①x 那么后一位可以无视A,B的限制了
②x=A,y 那么后一位可以无视B的限制了
③x 那么后一位可以无视A的限制了
④x=A,y=B
那么nothing will happen

所以我们设计dp[i][and][xor][_A][_B],最后两个参数为1或0,表示是/否需要满足A和B的限制.如此我们最后的答案是dp[30][1][1][1][1].

然后我们考虑dp[i][and][xor][A][B]的转移.
枚举第i位x和y的值.

x A条件 y B条件 x and y and条件 x xor y xor条件
0 满足 0 满足 0 满足 0 不一定
0 满足 1 不一定 0 满足 1 满足
1 不一定 0 满足 0 满足 1 满足
1 不一定 1 不一定 1 不一定 0 不一定

我们发现,各种条件可能会限制x和y的取值.具体来说,如果A,B,C的第i位分别是a,b,c的话(a,b,c=0或1),那么有:
x ≤ (_A?a:1)
y ≤ (_B?b:1)
x&y ≤ (and?c:1)
x^y ≥ (xor?c:0)

在确定了某一种x和y之后,我们就可以转移到后一位的dp了,后一位的4个限制条件转移如下:
newand = and&(x&y==c)
newxor = xor&(x^y==c)
new_A = _A&(x==a)
new_B = _B&(y==b)

然后就可以写出如下程序:

#include
#include
using namespace std;
typedef long long ll;

ll A,B,C,dp[35][2][2][2][2];

ll dfs(int i,int And,int Xor,int _A,int _B){
    if(i==-1)return 1;
    ll& ans=dp[i][And][Xor][_A][_B];
    if(ans>=0)return ans;
    ans=0;
    int a=(A>>i)&1,
        b=(B>>i)&1,
        c=(C>>i)&1;
    for(int x=0;x<=1;x++)
    for(int y=0;y<=1;y++)
    if(x<=(_A?a:1)
        &&y<=(_B?b:1)
        &&(x&y)<=(And?c:1)
        &&(x^y)>=(Xor?c:0))
    {
        ans+=dfs(i-1,And&((x&y)==c),Xor&((x^y)==c),_A&(x==a),_B&(y==b));
    }
    return ans;
}

void solve(){
    scanf("%lld%lld%lld",&A,&B,&C);
    memset(dp,-1,sizeof dp);
    printf("%lld\n",A*B-(dfs(30,1,1,1,1)-max(B-C+1,0ll)-max(A-C+1,0ll)));
}

int main(){
    int T;scanf("%d",&T);
    while(T--)solve();
}

你可能感兴趣的:([数位dp] Pair)