2019牛客暑期多校训练营(第七场)H-Pair 数位dp

62 篇文章 0 订阅

题目链接:https://ac.nowcoder.com/acm/contest/887/H

题意:

       给你数字A,B,C,询问你能找到多少对(x,y),使得x->[1,A],y->[1,B],并且x&y>C和x^y<C两个条件中至少满足一个。

做法:

       由于要求上述两个条件中至少满足一个,在处理上需要去重会有些麻烦,所以我们考虑取反,用A*B-sub,sub是同时满足x&y<=C和x^y>=C的数的个数。

       好像蛮典型的数位dp的,dp[pos][la][lb][and][xor]表示,数字a是否达到上限la,数字b是否达到上限lb,&是否满足条件and,^是否满足条件xor,下面的操作就是正常的数位dp做的了。。根据是否需要达到要求规定范围。但是这里最后要减掉max(0ll,A-C+1)和max(0ll,B-C+1),因为在上述条件下,在x=0,y>=C的对数也会被算入,但是题目要求x最低是1,所以要减掉在B大于等于C时,C到B的那些数,y=0的时候同理。


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
const int maxn=15;
typedef long long ll;
typedef pair<int,int> pii;
ll dp[35][2][2][2][2];
ll A,B,C;
ll dfs(int pos,int la,int lb,int And,int Xor){
    if(pos<0) return 1;
    if(dp[pos][la][lb][And][Xor]!=-1) return dp[pos][la][lb][And][Xor];
    ll ret=0;
    int na=1,nb=1,nand=1,nxor=0;
    if(la) na=(A>>pos&1);
    if(lb) nb=(B>>pos&1);
    if(And) nand=(C>>pos&1);
    if(Xor) nxor=(C>>pos&1);
    for(int i=0;i<=na;i++){
        for(int j=0;j<=nb;j++){
            if((i&j)>nand) continue;
            if((i^j)<nxor) continue;
            ret+=dfs(pos-1,la&&(i==na),lb&&(j==nb),And&&((i&j)==nand),Xor&&((i^j)==nxor));
        }
    }
    return dp[pos][la][lb][And][Xor]=ret;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%lld",&A,&B,&C);
        memset(dp,-1,sizeof(dp));
        ll ans=dfs(30,1,1,1,1);
        ans-=max(0ll,A-C+1);
        ans-=max(0ll,B-C+1);
        printf("%lld\n",1ll*A*B-ans);
    }
    return 0;
}
/*

*/

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值