• 2019 牛客网 第七场 H pair


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

     题意:

    给定A,B,C问在[1,A]和[1,B]中有多少对x,y满足x&y>C或者x^y<C.

    数据范围:1<=A,B,C<=1e9,测试用例 T<=100 .

    思路:

    根据题意本题可以用数位dp来解。

    /* and   0&0=0;  0&1=0;   1&0=0;    1&1=1;
       xor   0 ^ 0=0;  0 ^ 1=1;  1^ 0=1;   1^1=0;
       
    3
    3 4 2  
    4 5 2    
    7 8 5
    
    5
    7
    31
    
    1<=x<=a 
    1<=y<=b 
    
    x&y>c x^y<c 
    
    */
    
    #include <iostream>
    #include <bitset>
    using namespace std;
    int main()
    {
        int t;
        cin>>t;
        while(t--)
        {
        int a,b,c,ans=0;
        cin>>a>>b>>c;
        for(int i=1;i<=a;i++)
            for(int j=1;j<=b;j++)
            {
                if((i^j)<c) 
                {
                    ans++; 
                    cout<<(i^j)<<" ";
                    bitset<8> aa(i);
                    bitset<8> bb(j);
                    bitset<8> cc(i^j);
                    cout<<aa<<" "<<i<<endl;
                    cout<<bb<<" "<<j<<endl;
                    cout<<cc<<" "<<(i^j)<<endl<<endl;
                }    
            } 
        cout<<ans<<endl;    
        }
        return 0;
    } 
    /*
    7 8 5
    0 3 2 4 3 0 1 4 2 1 0 4 0 1 2 3 4 1 0 3 2 4 2 3 0 1 4 3 2 1 0 31
    
    0 3 2 4 3 0 1 4 2 1 0 4 0 1 2 3 4 1 0 3 2 4 2 3 0 1 4 3 2 1 0 31
    
    0 3 2 4 3 0 1 4 2 1 0 4 0 1 2 3 4 1 0 3 2 4 2 3 0 1 4 3 2 1 0 0 32
    
    0 3 2 4 3 0 1 4 2 1 0 0 1 2 4 1 0 3 4 2 3 0 22
    
    0 3 2 3 0 1 2 1 0 0 10
    
    0 0 0 0 0 0 0 7
    
    0 0 1 1 0 0 1 1 0 0 1 1 0 13
    
    0 2 0 1 2 1 0 0 1 2 1 0 2 2 0 1 2 1 0 19
    
    0 3 2 3 0 1 2 1 0 0 1 2 3 1 0 3 2 2 3 0 1 3 2 1 0 25
    
    0 3 2 4 3 0 1 4 2 1 0 4 0 1 2 3 4 1 0 3 2 4 2 3 0 1 4 3 2 1 0 31
    
    */
    /*
    00000001
    00000001
    0
    00000010
    00000010
    0
    00000010
    00000011
    1
    00000011
    00000010
    1
    00000011
    00000011
    0
    00000100
    00000100
    0
    00000100
    00000101
    1
    
    7
    */
    
    
    /*
    00000001 00000010 0 3
    00000001 00000100 0 5
    00000001 00000011 1 2
    00000010 00000001 0 3
    00000010 00000100 0 6
    00000011 00000100 0 7
    00000011 00000001 1 2
    */
    
    /*
    x&y>c x^y<c 2 
    00000001 00000001 1 0
    00000010 00000010 2 0
    00000010 00000011 2 1
    00000011 00000010 2 1
    00000011 00000011 3 0
    
    */
     /*
     /*
    00000001 00000100 0 5
    00000001 00000110 0 7
    00000001 00000111 1 6
    00000001 00001000 0 9
    00000010 00000100 0 6
    00000010 00000101 0 7
    00000010 00000111 2 5
    00000010 00001000 0 10
    00000011 00000100 0 7
    00000011 00000101 1 6
    00000011 00000110 2 5
    00000011 00001000 0 11
    00000100 00000001 0 5
    00000100 00000010 0 6
    00000100 00000011 0 7
    00000100 00001000 0 12
    00000101 00000010 0 7
    00000101 00000011 1 6
    00000101 00001000 0 13
    00000110 00000001 0 7
    00000110 00000011 2 5
    00000110 00001000 0 14
    00000111 00000001 1 6
    00000111 00000010 2 5
    00000111 00001000 0 15
    */
    
    /*
                                x&y>c x^y<c 
    00000001 00000001 1 0
    00000001 00000010 0 3
    00000001 00000011 1 2
    00000001 00000101 1 4
    
    00000010 00000001 0 3
    00000010 00000010 2 0
    00000010 00000011 2 1
    
    00000010 00000110 2 4
    
    00000011 00000001 1 2
    00000011 00000010 2 1
    00000011 00000011 3 0
    00000011 00000111 3 4
    00000100 00000100 4 0
    00000100 00000101 4 1
    00000100 00000110 4 2
    00000100 00000111 4 3
    
    00000101 00000001 1 4
    
    00000101 00000100 4 1
    00000101 00000101 5 0
    00000101 00000110 4 3
    00000101 00000111 5 2
    
    00000110 00000010 2 4
    
    00000110 00000100 4 2
    00000110 00000101 4 3
    00000110 00000110 6 0
    00000110 00000111 6 1
    
    00000111 00000011 3 4
    00000111 00000100 4 3
    
    00000111 00000101 5 2
    00000111 00000110 6 1
    
    00000111 00000111 7 0
    
    */
     */
    分析题意的代码

     大佬的解析:

    题意:给你 三个数A,B,C让你从  1~A内选择一个X,1~B内选择一个Y

    找出<X,Y>的对数,使得 X&Y > C  ||  X^Y < C

    平常做的数位dp都是从一个区间内找数的个数,而现在是从两个区间找数对的对数,那就在之前的三维dp上多开两维,之前是枚举一个区间上限,现在改成两个区间上限,其他的和普通的数位dp一样

    注意数位dp找到的是从0开始的  0 & Y 和 X&0  一定都小于C 不需要管

    但是  0 ^ Y 和 X^0  当Y  和  X  小于C时  是满足的  所以结果还需要减去 ^运算  X 或  Y  为零时的情

    原文链接:https://blog.csdn.net/Daxian911/article/details/99462695

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <map>
    #include <vector>
    #include <queue>
    using namespace std;
    #define ll long long
    #define int long long
    const int maxn=1e5+5;
    ll dp[32][3][3][2][2];
    int a[32], b[32],c[32];
    ll dfs1(int pos, int And, int Xor, int limit1, int limit2){
        if(pos == -1) {
            if(And == 1 || Xor == 2) return 1;
            return 0;
        }
        if(dp[pos][And][Xor][limit1][limit2] != -1) return dp[pos][And][Xor][limit1][limit2];
        int up1 = limit1 ? a[pos] : 1;
        int up2 = limit2 ? b[pos] : 1;
        ll ans = 0;
        for(int i = 0; i <= up1; i++) {
            for(int j = 0; j <= up2; j++) {
                int x = i & j, y = i ^ j;
                int _and = And;
                int _xor = Xor;
                if(And == 0) {
                    if(x > c[pos]) _and = 1;
                    else if(x < c[pos]) _and = 2;
                }
                if(Xor == 0) {
                    if(y > c[pos]) _xor = 1;
                    else if(y < c[pos]) _xor = 2;
                }
                ans += dfs1(pos - 1, _and, _xor, (i == up1) && limit1, limit2 &&(j == up2));
            }
        }
        dp[pos][And][Xor][limit1][limit2] = ans;
        return ans;
    }
    signed main()
    {
        int T, A, B, C;
        scanf("%lld", &T);
     
        while(T--) {
            memset(dp, -1, sizeof(dp));
            scanf("%lld%lld%lld", &A, &B, &C);
            int i = 0, j = 0, k = 0;
            int ans = min(C - 1, A) + min (C - 1, B) + 1;
            while(i < 30) {
                a[i++] = (A & 1); A>>=1;
            }
            while(j < 30) {
                b[j++] = (B & 1); B>>=1;
            }
            while(k < 30) {
                c[k++] = (C & 1); C>>=1;
            }
            printf("%lld
    ", dfs1(29,0,0,1,1) - ans);
        }
        return 0;
    }
    大佬的代码,我直接复制了

    另外一个大佬的代码解析:

    思路:数位dp,定义一个dp[len][lim1][lim2][ok1][ok2][za][zb]。

    len代表当前枚举的二进制位,lim1,lim2分别代表x和y的上限,

    ok1代表对于x&y>C是否成立,成立是1,有可能成立是0,不可能成立是-1,

    ok2代表对于x^y<C是否成立,1代表不成立,0代表可能成立,-1代表成立,

    za和zb分别代表x和y的数中是否出现了1,因为x和y的二进制位不能是全零。转移的话代码中非常容易看懂。
    原文链接:https://blog.csdn.net/qq_43316231/article/details/98968980

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int t;
    ll A,B,C,f[50],g[50],h[50];
    ll dp[66][2][2][3][3][2][2];
    ll dfs(ll len,ll lim1,ll lim2,ll ok1,ll ok2,ll za,ll zb){
        ll &ret=dp[len][lim1][lim2][ok1+1][ok2+1][za][zb];
        if(ret!=-1)return ret;
        if(len==0){
            ret=za&&zb&&(ok1>0||ok2<0);
            return ret;
        }
        ret=0; --len;
        int up1=lim1?1:f[len];
        int up2=lim2?1:g[len];
        for(int x=0;x<=up1;x++)for(int y=0;y<=up2;y++)
        ret+=dfs(len,lim1|(x<f[len]),lim2|(y<g[len]),ok1==0?(x&y)-h[len]:ok1,ok2==0?(x^y)-h[len]:ok2,za|x,zb|y);
        return ret;
    }
    int main(){
        scanf("%d",&t);
        while(t--){
            memset(dp,-1,sizeof(dp));
            scanf("%lld%lld%lld",&A,&B,&C);
            for(int i=0;i<30;i++){
                f[i]=A%2,A/=2;
                g[i]=B%2,B/=2;
                h[i]=C%2,C/=2;
            }
            printf("%lld
    ",dfs(31,0,0,0,0,0,0));
        }
        return 0;
    }
    另一个大佬的,我又复制了

     结合大佬的

    我自己水了一遍,嘻嘻

    /* and   0&0=0;  0&1=0;   1&0=0;    1&1=1;
       xor   0 ^ 0=0;  0 ^ 1=1;  1^ 0=1;   1^1=0;
    3 4 2 
    5 2   
    8 5
    7
    1<=x<=a
    1<=y<=b
    条件 x&y>c x^y<c 分析得 只满足x^y<c 即可。
    a,b,c<1e9 转换为二进制数位是30个。
    */
     
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
     
    LL dp[32][3][3][2][2], a[32],b[32],c[32];
     
    LL dfs(LL len, LL And, LL Xor, LL limit1, LL limit2){
        if(len==-1)
        {
            if(And == 1 || Xor == 2) return 1ll;
            return 0;
        }
        //满足条件,直接返回最底层答案。
        if(dp[len][And][Xor][limit1][limit2] != -1)
            return dp[len][And][Xor][limit1][limit2];
        //没到达上界并且数位已经统计过,直接返回数量。
         
        LL up1 = limit1 ? a[len] : 1;
        LL up2 = limit2 ? b[len] : 1;
        LL cnt =0;
          for(LL i = 0; i <= up1; i++) {
            for(LL j = 0; j <= up2; j++) {
                LL x = i & j, y = i ^ j;
                LL _and = And;
                LL _xor = Xor;
                if(And == 0) {
                    if(x > c[len]) _and = 1;
                    else if(x < c[len]) _and = 2;
                }
                if(Xor == 0) {
                    if(y > c[len]) _xor = 1;
                    else if(y < c[len]) _xor = 2;
                }
                cnt += dfs(len-1,_and,_xor,(i == up1) && limit1,limit2 &&(j==up2));
            }
        }
        dp[len][And][Xor][limit1][limit2]=cnt;
        return cnt;
    }
     
    LL solve(LL aa,LL bb,LL cc)
    {
        LL ans=min(cc - 1, aa) + min (cc - 1, bb) + 1;
        for(LL i=0;i<30;i++){
            a[i]=(aa & 1); aa>>=1;
            b[i]=(bb & 1); bb>>=1;
            c[i]=(cc & 1); cc>>=1;
            //b[i]=bb%2,bb/=2;
            //c[i]=cc%2,cc/=2;
            }
        return (dfs(29,0,0,1,1)-ans);
    }
     
    int main()
    {
        int t;
        cin>>t;
        while(t--)
        {
            memset(dp, -1, sizeof(dp));
            LL a,b,c;
            cin>>a>>b>>c;
            cout<<solve(a,b,c)<<endl;   
        }
        return 0;
    }
    View Code
  • 相关阅读:
    又见Alice and Bob
    算法7-6:图的遍历——广度优先搜索
    算法7-5:图的遍历——深度优先搜索
    水池数目
    过河问题
    括号配对问题
    C# 客户端判断是否安装office03、07或WPS
    C# json
    开源cms
    可执行代码(Executable Code)目标代码(object code)
  • 原文地址:https://www.cnblogs.com/young-children/p/11351478.html
Copyright © 2020-2023  润新知