6-1. Code For 1

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 &lt; = 2 , 3 &lt; 2 2 2^1&lt;=2,3&lt;2^2 ,所以2,3最后分成的长度为 2 0 + 2 1 = 3 2^0+2^1=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 = &lt; 4 , 5 , 6 , 7 &lt; 2 3 2^2=&lt;4,5,6,7&lt;2^3 ,所以4,5,6,7最后分成的长度为 2 0 + 2 1 + 2 2 = 7 2^0+2^1+2^2=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 &lt; = x &lt; 2 n + 1 2^n&lt;=x&lt;2^{n+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
下面我们就可以通过二分来做题了。

#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;
}

猜你喜欢

转载自blog.csdn.net/Liusyu6688/article/details/85012355