分治之替换

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37220238/article/details/80586608
总时间限制: 
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;
}

猜你喜欢

转载自blog.csdn.net/qq_37220238/article/details/80586608