数位dp hdu5598 GTW likes czf

本文介绍了一道涉及位运算的编程题解法,通过数学推导将问题转化为求特定区间内满足条件的数对数量。利用数位DP进行高效求解,并给出了完整的代码实现。

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

传送门:点击打开链接

题意:告诉l,r,g,t,每次在[l,r]区间内选一个数字,再从g,t这两个数字里面选一个数字,求这两个数字的@值。定义x@y=((x&y)|y)^x

思路:首先,我们能够证明,x@y=((x&y)|y)^x其实就等于x^y,这算是第一个绕圈把

然后呢,我们再考虑到,一个数x对n个不同的数字求位异或,那么位异或的结果一定都不同。

所以,题目可以转换成(r-l+1)*2-num,num是满足如下条件的a和b的对数

a,b属于[l,r], 且 a^g=b^t,我们稍微变形一下,就得到a^b=g^t

所以最后题目转换成了求[l,r]中有多少对(a,b),使得a^b=g^t


因为区间有左端,我们可以通过容斥进一步的简化题目。

设S(m,n)函数能求出a在区间[1,m],b在区间[1,n],然后a^b=g^t的对数

那么要求a在区间[l,r],b在区间[l,r],然后求a^b=g^t的个数就可以转换成S(r,r)-S(l-1,r)*2+S(l-1,l-1),原理是容斥定理,可以仔细去想想

到上面的步数,应该都是很容易就能看懂的,问题是S(m,n)函数要如何来实现

这里我们用数位dp来实现


那么对于S(a,b)

设dp[n][i][j]表示正在考虑第n位bit位

i为0表示a的第n位没有被限制,1表示a的第n位被限制了

j为0表示b的第n位没有被限制,1表示b的第n位被限制了

那么动态转移方程就能写出来了,我的dp写的有点搓,不要在意这些细节...
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;

const int MX = 100 + 5;
const int mod = 1e9 + 7;

int na[MX], nb[MX], nx[MX];
LL l, r, g, t, dp[MX][2][2];

LL S(LL a, LL b, LL x) {
    memset(dp, 0, sizeof(dp));
    for(int i = 63; i >= 0; i--) {
        na[i] = a >> i & 1;
        nb[i] = b >> i & 1;
        nx[i] = x >> i & 1;
    }

    dp[63][1][1] = 1;
    for(int i = 62; i >= 0; i--) {
        if(na[i] ^ nb[i] == nx[i]) {
            dp[i][1][1] += dp[i + 1][1][1];
        }

        dp[i][1][0] += dp[i + 1][1][0];
        if(nb[i] && na[i] == nx[i]) dp[i][1][0] += dp[i + 1][1][1];

        dp[i][0][1] += dp[i + 1][0][1];
        if(na[i] && nb[i] == nx[i]) dp[i][0][1] += dp[i + 1][1][1];

        dp[i][0][0] += dp[i + 1][0][0] * 2;
        if(na[i]) dp[i][0][0] += dp[i + 1][1][0];
        if(nb[i]) dp[i][0][0] += dp[i + 1][0][1];
        if(na[i] && nb[i] && !nx[i]) dp[i][0][0] += dp[i + 1][1][1];
    }
    return dp[0][0][0] + dp[0][0][1] + dp[0][1][0] + dp[0][1][1];
}

LL solve() {
    LL ret = (r - l + 1) * 2, x = g ^ t;
    ret -= S(r, r, x) - S(l - 1, r, x) * 2 + S(l - 1, l - 1, x);
    return ret % mod;
}

int main() {
    int T; //FIN;
    scanf("%d", &T);
    while(T--) {
        scanf("%I64d%I64d%I64d%I64d", &l, &r, &g, &t);
        printf("%I64d\n", solve());
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值