牛客练习赛83 B. 计算几何(数位DP/找规律)
链接:https://ac.nowcoder.com/acm/contest/11173/B
来源:牛客网
题目描述
由于小 LL 追女孩子的时间不多了,于是这里只有简单版题意。
共有 TT 组询问,每次给定 l,rl,r ,你需要求出在 [l,r][l,r] 中有多少个数在二进制下 11 的个数有奇数个。
例如 4=(100)24=(100)2 ,在二进制下有 11 个 11 ,那么如果 l=r=4l=r=4 ,答案为 11 。
例如 5=(101)25=(101)2 ,在二进制下有 22 个 11 ,那么如果 l=r=5l=r=5 ,答案为 00 。
输入描述:
第一行一个整数 TT 。接下来 TT 行,每行两个整数 l,rl,r 。
输出描述:
输出 TT 行,每行 11 个整数代表答案。
示例1
输入
复制
1
3 4
输出
复制
1
说明
(3)10=(11)2,(4)10=(100)2(3)10=(11)2,(4)10=(100)2 ,那么在 [3,4][3,4] 中只有 44 在二进制下有奇数个 11 。
备注:
1≤T≤10,1≤l≤r≤10181≤T≤10,1≤l≤r≤1018 。
这个题可以打表找规律也可以推一下也可以数位dp一下,我选择数位dp。dp数组的含义是\(dp[i][j][k]\)表示处理到第i位,之前是否紧贴边界,从第i位到第1位1的个数为奇数(k = 1) or 为偶数(k = 0)的数的个数。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a, b, num[66], len;
int dp[66][2][2];
int dfs(int len, int flag, int odd_even) {//参数为从高到低当前处理到第几位 范围是1到len flag表示之前的部分是否紧贴边界
//odd_even表示该状态后1的个数 为偶数0/为奇数1
if(len == 0) {
return 1;
}
if(dp[len][flag][odd_even] != -1) return dp[len][flag][odd_even];
int ans = 0;
for(int i = 0; i < 2; i++) {
if(!flag || (flag && i <= num[len])) {
if(i == 0) {
if(odd_even == 1) {
if(len != 1) ans += dfs(len - 1, flag && (i == num[len]), odd_even);//这一位为0,不改变之前1的奇偶个数
else ans += 0;
}
else if(odd_even == 0) {
if(len == 1) ans++;
else ans += dfs(len - 1, flag && (i == num[len]), odd_even);
}
} else {
if(odd_even == 0) {
if(len != 1) ans += dfs(len - 1, flag && (i == num[len]), 1);
}
else {
if(len != 1) ans += dfs(len - 1, flag && (i == num[len]), 0);
else ans++;
}
}
}
}
return (dp[len][flag][odd_even] = ans);
}
int calc(int x) {
memset(num, 0, sizeof(num));
len = 0;
while(x) {
int now = x % 2;
x /= 2;
num[++len] = now;
}
for(int i = 0; i < 65; i++) {
for(int k = 0; k < 2; k++) {
for(int w = 0; w < 2; w++) {
dp[i][k][w] = -1;
}
}
}
int ret = dfs(len, 1, 1);
return ret;
}
signed main() {
int t;
cin >> t;
while(t--) {
cin >> a >> b;
cout << calc(b) - calc(a - 1) << endl;
}
return 0;
}