bzoj 3329 Xorequ 数位dp+矩阵乘法

博客围绕一个方程展开,对于方程 x xor 3x = 2x ,通过打出小规模解发现 x 转换成二进制后任意相邻两个二进制位不都为 1 。求 [1,n] 中满足条件的数用数位 dp ,求 [1,2n] 满足性质的数的个数可通过矩阵乘法,时间复杂度为 O(Tlogn) 。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

题目传送门

解法

比较简单的数位dp

  • 表示 x xor 3x=2x x   x o r   3 x = 2 x 这个方程并不会解,那么我们把小规模的解 x x 打出来看看?
  • 然后我们就发现,x满足这样一个条件:转换成二进制后任意相邻两个二进制位不都为1
  • 对于求 [1,n] [ 1 , n ] 中有多少个满足这个条件的数直接数位dp一下就可以了
  • 然后考虑如何求 [1,2n] [ 1 , 2 n ] 满足这个性质的数的个数
  • f[i][0] f [ i ] [ 0 ] 表示二进制下第 i i 位为0且合法的数, f[i][1] f [ i ] [ 1 ] 同理
  • 转移十分简单, f[i][0]=f[i1][0]+f[i1][1],f[i][1]=f[i1][0] f [ i ] [ 0 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] , f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ]
  • 发现这个可以矩阵乘法,然后就做完了
  • 时间复杂度: O(Tlogn) O ( T log ⁡ n )

代码

#include <bits/stdc++.h>
#define Mod 1000000007
#define LL long long
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct solve1 {
    LL a[110], f[110][2][2];
    LL dp(int i, int las, int l) {
        if (i == 0) return 1;
        if (!l && f[i][las][l] != -1) return f[i][las][l];
        LL ret = 0, x = l ? a[i] : 1;
        for (int j = 0; j <= x; j++) {
            if (las && j) continue;
            ret += dp(i - 1, j, l && j == a[i]);
        }
        if (!l) f[i][las][l] = ret;
        return ret;
    }
    LL solve(LL n) {
        memset(f, -1, sizeof(f));
        int len = 0;
        while (n) a[++len] = n % 2, n /= 2;
        return dp(len, 0, 1) - 1;
    }   
} sol1;
struct Matrix {
    int a[3][3];
    void Clear() {memset(a, 0, sizeof(a));} 
};
Matrix operator * (Matrix x, Matrix y) {
    Matrix ret; ret.Clear();
    for (int k = 1; k <= 2; k++)
        for (int i = 1; i <= 2; i++)
            for (int j = 1; j <= 2; j++)
                ret.a[i][j] = (ret.a[i][j] + 1ll * x.a[i][k] * y.a[k][j] % Mod) % Mod;
    return ret;
}
Matrix operator ^ (Matrix x, LL y) {
    Matrix ret = x; y--;
    while (y) {
        if (y & 1) ret = ret * x;
        y >>= 1, x = x * x;
    }
    return ret;
}
LL solve(LL n) {
    Matrix tx; tx.Clear();
    tx.a[1][1] = tx.a[1][2] = tx.a[2][1] = 1;
    tx = tx ^ n;
    Matrix ty; ty.Clear(); ty.a[1][1] = 1;
    ty = tx * ty;
    return (ty.a[1][1] + ty.a[2][1]) % Mod;
}
int main() {
    int T; read(T);
    while (T--) {
        LL n; read(n);
        cout << sol1.solve(n) << "\n" << solve(n) << "\n";
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值