-
总时间限制:
- 1000ms 内存限制:
- 65536kB
-
描述
-
对于一个大于1的整数X,,你可以进行以下替换操作:
删掉它,把它换成三个数:⌊X/2⌋,X%2,⌊X/2⌋。
现在给你一个数列,开始数列只有一个整数N,你每次可以在数列中选择一个大于1的整数进行上述操作,直到这个数列最后全是1或0为止。
接下来,在最后的数列中查询区间[L,R]中有多少个1。输出1的个数。
0<N<=250,0<=R-L<=100000,并保证R不超过最终数列的长度。
输入
- 一行三个整数,分别表示N,L,R。 输出
- 一个整数,表示[L,R]区间有多少个1。 样例输入
-
10 3 10
样例输出
-
5
提示
-
【样例说明】
{10}→
{5 0 5}→
{2 1 2 0 5}→
{1 0 1 1 2 0 5}→
{1 0 1 1 1 0 1 0 5}→
{1 0 1 1 1 0 1 0 2 1 2}→
{1 0 1 1 1 0 1 0 1 0 1 1 2}→
{1 0 1 1 1 0 1 0 1 0 1 1 1 0 1}
- 解题思路:
这道题的数据范围为2^50,显然不能够使用整型数组来存放分解后所有的数字。观察一下规律,每一个数字都会被分解成三个数字,且最终都会被分解成0或1,所以可以考虑使用分治递归的方法来做,每个数字都会被一直分解成三个数字,一直分解到0或1.
虽然说无法使用数组来存放所有的数字,但是我们是可以算出这个数列的长度的。观察一下上面分解10的时候的规律{10}->{5,0,5}->{2,0,2,0,2,0,2}->{1,0,1,1,1,0,1,0,1,0,1,1,1,0,1},第1次长度为1,第2次长度为3,第3次长度为7,第4次长度为15,找到规律长度为2^(分解次数+z-1,分解次数就是n除以2最终得到1或者0的除的次数。可以计算出数列的长度len
那么(1,len)就是分治递归的范围。因为每一个数都会被分解成三个数,所以可以这样写
return dg(n/2,l,mid-1)+dg(n%2,mid,mid)+dg(n/2,mid+1,r);
参考程序
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
LL n,L,R,len=1,x;
int tot;
int dg(LL n,LL l,LL r)
{
if(R<l||L>r||n==0)//在区间(L,R)之外或者为0(出口1)
return 0;
if(n==1)//为1且在区间内就返回1(出口2)
return 1;
LL mid=(l+r)/2;//分治
return dg(n/2,l,mid-1)+dg(n%2,mid,mid)+dg(n/2,mid+1,r);
}
int main()
{
cin>>n>>L>>R;//输入区间(L,R)
x=n;
while(x>1)//计算出最后数组的长度len
{
x/=2;
tot++;
}
len=pow(2,tot+1)-1;
printf("%d",dg(n,1,len));//分治区间为(1,len)
return 0;
}