Jon fought bravely to rescue the wildlings who were attacked by the white-walkers at Hardhome. On his arrival, Sam tells him that he wants to go to Oldtown to train at the Citadel to become a maester, so he can return and take the deceased Aemon's place as maester of Castle Black. Jon agrees to Sam's proposal and Sam sets off his journey to the Citadel. However becoming a trainee at the Citadel is not a cakewalk and hence the maesters at the Citadel gave Sam a problem to test his eligibility.
Initially Sam has a list with a single element n. Then he has to perform certain operations on this list. In each operation Sam must remove any element x, such that x > 1, from the list and insert at the same position x/2,x%2 ,x/2 sequentially. He must continue with these operations until all the elements in the list are either 0 or 1.
Now the masters want the total number of 1s in the range l to r (1-indexed). Sam wants to become a maester but unfortunately he cannot solve this problem. Can you help Sam to pass the eligibility test?
输入:
The first line contains three integers n, l, r (0 ≤ n < 250, 0 ≤ r - l ≤ 105, r ≥ 1, l ≥ 1) – initial element and the range l to r.
It is guaranteed that r is not greater than the length of the final list.
输出:
Output the total number of 1s in the range l to r in the final sequence.
样例输入:
7 2 5
样例输出:
4
样例输入:
10 3 10
样例输出:
5
注释:
Elements on positions from 2-nd to 5-th in list is [1, 1, 1, 1]. The number of ones is 4.
Elements on positions from 3-rd to 10-th in list is [1, 1, 1, 0, 1, 0, 1, 0]. The number of ones is 5.
大意:一个数n 如果大于1,就用n/2,n%2,n/2这个序列替换它,直到最后整个序列全由0和1组成,
任务求是在这个序列中[l,r]范围内数的和;
例如:
1 1(最小规模)
2 1 0 1
3 1 1 1
4 2 0 2 101 0 101(2)
5 2 1 2 101 1 101(2)
6 3 0 3 1 1 1 0 1 1 1(3)
7 3 1 3 1 1 1 1 1 1 1 (3)
8 4 0 4 202 0 202(4) 101 0 101 0 101 0 101
......
对此我们可以发现规律
一个数n左右两边是n/2的序列,中间则取决于自己是奇数还是偶数
由此,可以不断缩小范围;
步骤:
1,求得序列总长len
2,在1~len范围内处理序列直到规模缩小到1
#include<iostream>
#include<cstdio>
using namespace std;
long long l, r;//所求范围
long long divide(long long n, long long left, long long right)
{
if (left>r || right<l) return 0;//不在所求范围,相当于加0;
if (n == 1) return 1;//得到最小规模
long long mid = (left + right) >> 1;//得到中间的位置
long long k = 0;//中间的数要用long long
if (n % 2 == 1&&mid>=l&&mid<=r)k++;//%2的数在所求范围内并且为奇数
return ( k + divide(n >> 1, left, mid - 1) + divide(n >> 1, mid + 1, right));//缩小范围
}
int main()
{
long long n, ans;
cin >> n >> l >> r;
//求对于n这个数被分解后的序列总长
long long len = 1, t = n;
while (t>1)
{
len= len * 2 + 1;
t >>= 1;//2的几次方就执行几次该操作
}
//cout << len;
//分而治之并不断递归
ans=divide(n, 1, len);
cout << ans;
return 0;
}
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
ll l, r;
ll divide(ll n, ll left, ll right)
{
if (left > r || right < l) return 0;
if (n == 1) return 1;
int mid = (left + right) / 2;
int t = 0;
if (n % 2 && (mid >= l && mid <= r)) t++;
return (t + divide(n >> 1, left, mid - 1) + divide(n >> 1, mid + 1, right));
}
int main()
{
ll n, k, len = 1, ans;
cin >> n >> l >> r;
k = n;
while (k > 1) {
len = len * 2 + 1;
k >>= 1;
}
ans = divide(n, 1, len);
cout << ans;
}