[二分] cf 768B Code For 1

CF768B题解
本文解析了Codeforces竞赛中的CF768B题目,介绍了一种利用二进制特性简化问题的方法,并给出了两种实现思路:直接计算法和递归法。通过对二进制位的巧妙操作,实现了高效求解。

[二分] cf 768B Code For 1

@(ACM题目)[二分图]

比赛时各种zz,搞了2h才搞出来(ノಠ益ಠ)ノ彡┻━┻,最后用的是递归求解。之后看了别人的才发现很简洁的规律(二进制无敌!进制无敌!无敌!无敌!敌!)。

以后需要注意:与复制有关的要考虑二进制

题意

将一个数n拆成数列n2, nmod2, n2,重复对数列中大于1的数字进行同样的拆操作,直到整个数列变成01串。问最后得到的数列中,[l, r]之间有多少个1

题意转换

拆的操作即为n>>1,n&1,n>>1,其中n&1即n在二进制下的末位,而去掉这个末位得到的就是n>>1。也就是要将n的二进制表示中的最低位放在中间,剩余的二进制位在这个最低位左右各放一份,继续进行拆操作。
设数字n对应的二进制表示为abcde,其中a、b、c、d、e为0或1(如22表示为10110),则对应“拆”的过程为:
- 以e为中心:abcd, e, abcd
- 以d为中心:abc, d, abc, e, abc, d, abc
- 以c为中心:ab, c, ab, d, ab, c, ab, e, ab, c, ab, d, ab, c, ab
- 以b为中心:a, b, a, c, a, b, a, d, a, b, a, c, a, b, a, e, a, b, a, c, a, b, a, d, a, b, a, c, a, b, a

规律

设n的二进制表示为a1a2...am,则最终得到的字符串中第i位的数字为alog2(lowbit(i))

#include<iostream>
#include<cstdio>
#include <cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<list>
#include<stack>
using namespace std;
typedef long long LL;
const int maxn = 55;
int bn[maxn];
int main()
{
    LL n, l, r;
    cin>>n>>l>>r;
    for(int i = log2(n); i >= 0; i--)
    {
        bn[i] = n&1;
        n >>= 1;
    }
    int res = 0;
    for(LL i = l; i <= r; i++) res += bn[(int)log2(i&-i)];
    cout<<res<<endl;
    return 0;
}
递归程序

f(i)代表第1到第i位有多少个1,递归地算。

#include<iostream>
#include<cstdio>
#include <cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<list>
#include<stack>
using namespace std;
typedef long long LL;
const int maxn = 55;
bool ck[maxn];
map<LL, int> mp;
LL f(LL n)
{
    LL lb = n&(-n);
    if(n==0) return 0;
    if(n==lb) return ck[mp[n]+1]+f(n-1);
    else if(((n+1)&(-n-1))==n+1)
        return ck[mp[n+1]]+2*f(n>>1);
    return f(n-lb)+f(lb);
}
int main()
{
    LL cur = 1;
    for(int i = 0; i <= 51; i++)
    {
        mp[cur] = i;
        cur<<=1;
    }
    LL n, l, r, res = 0;
    cin>>n>>l>>r;
    LL nn = n;
    int cnt = 0;
    while(nn)
    {
        cnt++;
        nn>>=1;
    }
    nn=n;
    while(nn)
    {
        ck[cnt--] = nn&1;
        nn>>=1;
    }
    cout<<f(r)-f(l-1)<<endl;
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值