题意:给你一个小于2^50的数,每次将这个数分成三个数 x/2,x%2,x/2,直到剩下的数全是0,1,然后给你一个询问L,R,问你这个区间里面1的个数有几个。
分析:通过观察可以发现,给你一个数n,分解后这个01串的长度为比他大的2^k-1,然后0只会出现在x%2的位置。所以,一开始,我们将ans设置成R-L+1,每次dfs,取一个中点mid(因为x%2只会出现在中间)如果当前n%2==0 而且mid在[L,R]内则ans--,(比赛的时候思路很清晰啊,怎么就写不出来额。。还是要加强训练啊)
以下是代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<cstring>
#include<string>
#include<vector>
#include<unordered_set>
#include<unordered_map>
#include<cmath>
using namespace std;
#define ll long long
typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
const int MAXN = 1000005;
const int MAXM = 1000005;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
const double FINF = 1e30;
ll ans;
ll x, y;
void dfs(ll l, ll r, ll n)
{
if (n == 0)return;
ll mid = (l + r) / 2;
if (n % 2 == 0)
{
if (mid >= x&&mid <= y)
{
ans--;
//cout << mid << endl;
}
}
//注意这里的条件,因为R-L<10^5所以可以这么做
if(mid-1>=x)dfs(l, mid - 1, n / 2);
if(mid+1<=y)dfs(mid + 1, r, n / 2);
}
int main()
{
ll num[60];
num[0] = 1;
for (int i = 1; i <= 59; ++i)
{
num[i] = num[i - 1] * 2;
}
ll n;
scanf("%I64d", &n);
ll pos = upper_bound(num, num + 60, n) - num;
ll tmp = (1LL << pos) - 1;
scanf("%I64d %I64d", &x, &y);
ans = y - x + 1;
if (n == 0)
{
printf("0\n");
return 0;
}
dfs(1, tmp, n);
printf("%I64d\n", ans);
}