[二分] 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;
}
CF768B题解

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

被折叠的 条评论
为什么被折叠?



