6.1
题目大意:
给一个数n,和一个区间[l,r] (r-l<1e5,n<2^50),每次需要把序列中大于1的数字分成(n/2,n%2,n/2)(其中n/2是向下取整),直到所有数变成0或1,问[l,r]区间内有多少个1。
注意一点:l和r都是大于1的,所以我们的下标是[l-1,r-1]。
之前我做这道题目的时候用的方法是直接模拟,发现能过matrix但是过不了cf,于是我在网上搜索了一下,发现这道题目考的知识点其实是二分。
我们先来看几个例子:
1->[1]
2->[1,0,1]
3->[1,1,1]
因为
2
1
<
=
2
,
3
<
2
2
2^1<=2,3<2^2
21<=2,3<22,所以2,3最后分成的长度为
2
0
+
2
1
=
3
2^0+2^1=3
20+21=3
4->[2,0,2]->[1,0,1,0,1,0,1]
5->[0,1,0]->[1,0,1,1,1,0,1]
6->[0,1,0]->[1,1,1,0,1,1,1]
7->[0,1,0]->[1,1,1,1,1,1,1]
因为
2
2
=
<
4
,
5
,
6
,
7
<
2
3
2^2=<4,5,6,7<2^3
22=<4,5,6,7<23,所以4,5,6,7最后分成的长度为
2
0
+
2
1
+
2
2
=
7
2^0+2^1+2^2=7
20+21+22=7
8->[0,1,0]->[4,0,4]->[1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]
所以我们发现了规律,当
2
n
<
=
x
<
2
n
+
1
2^n<=x<2^{n+1}
2n<=x<2n+1时,最终的长度为
2
0
+
2
1
+
2
2
+
.
.
.
+
2
n
=
2
n
+
1
−
1
2^0+2^1+2^2+...+2^n=2^{n+1}-1
20+21+22+...+2n=2n+1−1
下面我们就可以通过二分来做题了。
#include<iostream>
#include<vector>
using namespace std;
using ll=long long;
ll len=1;
ll n,l,r,ans;
void dfs(ll a,ll b,ll l,ll r,ll n)//a,b表示查找区间的范围,l,r表示需要的计算的范围。
{
if(a>b||l>r)
return;
ll mid=(a+b)/2;
if(l>mid)//全在右边
dfs(mid+1,b,l,r,n/2);
else if(r<mid)//全在左边
dfs(a,mid-1,l,r,n/2);
else//mid在l和r的中间
{
ans+=n%2;
dfs(a,mid-1,l,mid-1,n/2);
dfs(mid+1,b,mid+1,r,n/2);
}
}
int main()
{
cin>>n>>l>>r;
//求长度;
ll temp=n;
while(temp>=2)
{
len=len*2+1;
temp/=2;
}
dfs(1,len,l, r,n);
cout<<ans<<endl;
}