题目链接:http://codeforces.com/problemset/problem/768/B
B. Code For 1 time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output
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 , , 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?
Input
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
Output the total number of 1s in the range l to r in the final sequence.
Examples input
7 2 5
output
4
input
10 3 10
output
5
Note
Consider first example:
Elements on positions from 2-nd to 5-th in list is [1, 1, 1, 1]. The number of ones is 4.
For the second example:
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.
【分析】:
方法1 :
不难发现,这个分裂过程是一个二叉树,最后的序列就是中序遍历序列。
所以可以模拟二叉树中序遍历,但是树太深复杂度太高。
然而,这棵二叉树每次分裂都是对称的,所以可以用map映射子树,相同的子树就不用再访问了。
方法2:
通过这道题学到了二叉树中序遍历的一个规律。
画一棵二叉树,然后按中序遍历给每个节点标号。
会惊奇的发现,叶子节点那一层全是奇数(即1的倍数)
从下数第二层全是2的倍数
从下数第三层全是4的倍数。。。。
(这一步可以通过位运算 i&-i 直接得出编号i属于哪一层,例如i=12,i&-i = 4,即i在从下数第log2(4)+1层)
即,通过中序遍历的编号就能知道此节点在哪一层,从而知道此处是0还是1,累加进答案即可。
由于本题声明 r-l 不会超过1e5,所以方法2才可行!for(l -> r)
【代码-方法1】:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll,ll>M;
ll get1(ll x)//求得以x为根的二叉树有多少个1
{
ll res=0;
for(int i=0;x;i++){
if(x%2)res+=(1ll<<i);
x>>=1;
}
return res;
}
ll dfs(ll n,ll &r,ll &ans)//二叉树中序遍历
{
int k=log2(n);//求得n的二进制数长度-1
if(r>(1ll<<k)-1)//左子树全部包含在内,无需遍历
{
ans+=M[n/2];
r-=(1ll<<k)-1;
}
else if(r>0&&n>1)
dfs(n/2,r,ans);
if(r>0){
ans+=n%2;
r--;
}
if(r>(1ll<<k)-1)//右子树全部包含在内,无需遍历
{
ans+=M[n/2];
r-=(1ll<<k)-1;
}
else if(r>0&&n>1)
dfs(n/2,r,ans);
}
int main()
{
ll n,l,r,y;
while(cin>>n>>l>>r)
{
M.clear();
for(ll t=n;t;t>>=1)//预处理每个状态的子树中1的数量
M[t]=get1(t);
ll ans1=0,ans2=0;
dfs(n,r,ans1);
dfs(n,--l,ans2);
cout<<ans1-ans2<<endl;
}
}
【代码-方法2】:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>v;
int main()
{
ll n,l,r;
while(~scanf("%lld",&n)){
v.clear();
scanf("%lld%lld",&l,&r);
if(n==0){
puts("0");continue;
}
ll ceng=log2(n),ans=0;
while(n){
v.push_back(n%2);//每一层的值
n/=2;
}
for(ll i=l;i<=r;++i){
ans+=v[ceng-log2(i&-i)];//每一项的层数
}
cout<<ans<<endl;
}
return 0;
}