• 【算法


    https://codeforces.com/gym/102900/problem/C

    找数对(x,y),他们的二进制位与的结果一定是0,然后他们的贡献就是他们位或的msb的长度+1。
    状态为dp[dep][xlim][ylim][lzero]表示长度为dep的数对,x限制为xlim,y限制为ylim的数对,前导零状态为lzero的总贡献(不是数量)。
    lzero为1:表示前面放的都是一串0,msb还没有出现,这个时候假如决定在当前层放,会增加dep的贡献
    x为lim或者y为lim的状态,最多被扫log方次,假如不想记录也可以,但是最好还是记一下。多组询问再找到这些状态memset掉不迟。

    若lzero为1,则这类状态只会被扫log次的,不想记录也行。
    若lzero不为1,则说明前面已经有了msb了,现在就数一数“位与为0的有多少个数对”返回给上层调用就行。

    int X[32], Y[32];
    int dp[32][2][2][2];
    
    ll dfs(int dep, bool xlim, bool ylim, bool lzero) {
        if (dep == -1)
            return 1;
        if (dp[dep][xlim][ylim][lzero] != -1)
            return dp[dep][xlim][ylim][lzero];
        int maxx = xlim ? X[dep] : 1;
        int maxy = ylim ? Y[dep] : 1;
        ll ans = 0;
        for (int i = 0; i <= maxx; ++i) {
            for (int j = 0; j <= maxy; ++j) {
                if (i & j)
                    continue;
                int contri = 1;
                if (lzero && (i || j))
                    contri += dep;
                bool nxlim = (xlim && i == maxx);
                bool nylim = (ylim && j == maxy);
                bool nlzero = (lzero && ((i || j) == 0));
                ans += 1LL * contri * dfs(dep - 1, nxlim, nylim, nlzero);
            }
        }
        ans %= MOD;
        dp[dep][xlim][ylim][lzero] = ans;
        return ans;
    }
    
    void solve() {
        int x, y;
        scanf("%d%d", &x, &y);
        for (int i = 0; i <= 31; ++i) {
            X[i] = x & 1;
            Y[i] = y & 1;
            x >>= 1;
            y >>= 1;
        }
        memset(dp, -1, sizeof(dp));
        printf("%lld
    ", (dfs(31, 1, 1, 1) - 1 + MOD) % MOD);
    }
    

    给定三个整数 a,b,k ,计算满足 0<=x<a 和 0<=y<b 的位与结果小于 k(即(x&y)<k)的有序数对(x,y)的个数。
    https://acm.ecnu.edu.cn/problem/77/

    int X[32], Y[32], K[32];
    ll dp[32][2][2][2];
    
    ll dfs(int dep, bool xlim, bool ylim, bool klim) {
        if (dep == -1)
            return !(xlim || ylim || klim);
        if (dp[dep][xlim][ylim][klim] != -1)
            return dp[dep][xlim][ylim][klim];
        int maxx = xlim ? X[dep] : 1;
        int maxy = ylim ? Y[dep] : 1;
        ll ans = 0;
        for (int i = 0; i <= maxx; ++i) {
            for (int j = 0; j <= maxy; ++j) {
                if (klim && ((i & j) > K[dep]))
                    continue;
                bool nxlim = (xlim && i == maxx);
                bool nylim = (ylim && j == maxy);
                bool nklim = (klim && (i & j) == K[dep]);
                ans += dfs(dep - 1, nxlim, nylim, nklim);
            }
        }
        dp[dep][xlim][ylim][klim] = ans;
        return ans;
    }
    
    void solve() {
        int x, y, k;
        scanf("%d%d%d", &x, &y, &k);
        for (int i = 0; i <= 31; ++i) {
            X[i] = x & 1;
            Y[i] = y & 1;
            K[i] = k & 1;
            x >>= 1;
            y >>= 1;
            k >>= 1;
        }
        memset(dp, -1, sizeof(dp));
        printf("%lld
    ", dfs(31, 1, 1, 1));
    }
    

    对于上面两题,问的都是从0开始的某个数或者从00开始的某个数对,所以只需要调用一次,状态也直接简化为xlim和ylim。

    若要求[L,R]范围内的数对,就有可能要调用多次,这时假如继续像上面这样设,也可以但是会导致每次更换限制后要memset。另一种方法是不记录xlim和ylim为true的状态,这样子状态可以公用,记状态为“第i位放j作为x开头放k作为y的开头,两个状态都不受限制时的答案”,这样就和LR这些限制无关了。数位dp的复杂度是每个状态都会被遍历一次,然后乘上这个状态的转移带来的平均花费,一般都是几个log的事情。假如把状态信息记录太多而不进行约简,就会更像“暴力”,假如状态信息过于约简但都与lim有关,就会在更换lim多次询问时需要把与lim相关的都进行清空。

  • 相关阅读:
    聊天机器人遇到的难题
    Node.js应用程序一起使用Webpack的4个简单步骤
    AngularJS页面渲染完成之后执行DOM操作
    js实现取出数组中的最大数和最小数
    npm安装angular-ui-bootstrap和bower安装
    centos7离线安装nginx
    配置SSH免密登录,总是需要输入密码
    idea win 常用快捷键
    二、centos7在线搭建ceph之Filesystem
    k8s使用cephfs来挂载mysql数据
  • 原文地址:https://www.cnblogs.com/purinliang/p/14636896.html
Copyright © 2020-2023  润新知