先上一篇位运算好博客:https://blog.csdn.net/xiaoqiaxiaoqi/article/details/80543941
求1个数博文:https://blog.csdn.net/sillyxue/article/details/80287609
求0个数博文:https://blog.csdn.net/u011361880/article/details/78049623
再上题目如下:
题目描述
校园里有一个古老的树,树上挂着n盏灯。这些灯围成了树的形状,很好看。现在,这n盏灯,有的的开着的,有的是关着的。开着用1来表示,关着用0来表示。当这些灯形成一个完美组合时,会非常非常好看。为了让这个树状的灯形成完美组合,小明打算对这些灯进行m个操作。小明可以对这些灯中的某些连续的灯进行关灯、开灯或者取反(开的灯关;关的灯开)操作,也可以数一下有多少盏灯是开着的,多少盏灯是关着的。请把数灯的结果输出来。
输入
第一行:输入一个正整数n和一个正整数m
第二行:一个只含有0和1的长度为n的字符串,表示编号为1到n的灯的状态
接下来m行,每行有1个数o或者3个数o,a,b,如果o是0,代表数一下有多少个关着的灯;如果o是1,代表数一下有多少个开着的灯;如果o是2,代表将编号为a到b的灯(含a和b)进行取反操作;如果o是3,代表将编号为a到b的灯(含a和b)进行开灯操作;如果o是4,代表将编号为a到b的灯(含a和b)进行关灯操作。
输出
对于数灯的操作结果,各输出一行。
样例输入
5 5
00111
1
2 2 3
3 1 1
4 2 5
0
样例输出
3
4
提示
第一个操作:数开着的灯,共3个(3,4,5),输出3;
第二个操作:将第2-3之间的灯全部取反,得到01011
第三个操作:将第1-1之间的灯全部开灯,得到11011
第四个操作:将第4-5之间的灯全部关灯,得到10000
第五个操作:数关着的灯,共4个(2,3,4,5),输出4。
【数据范围】
50%的数据:n < 32,m < 100000
100%的数据:n < 64,m <= 1000000
标签
通过初赛 二进制 模拟
接下来做题了。
位运算没搞定,错误代码如下,需要接着搞:
#include<iostream>
using namespace std;
int y;
char x;
int n,m,a,b;
int main(){
cin>>n>>m;
for (int i=1;i<=n;i++)
{
cin>>x;
y=y*2+x-'0';
}
for (int i=1;i<=m;i++){
int o,count=0,num=y;
cin>>o;
/*
o=0:数y的0的个数;
o=1:数y的1的个数;
o=2:将a到b位取反;
o=3:将a到b位=1;
o=4:将a到b位=0;
*/
if (o==0)
{
for (int i=1;i<=n;i++)
{
if(0==num%2) count++;
num>>=1;
}
cout<<count<<endl;
}
else if (o==1)
{
cout<<num<<endl;
while (num)
{
num = num&(num - 1);
count++;
}
cout<<count<<endl;
}
else if (o==2)
{
cin>>a>>b;
num=~num;
cout<<num<<endl;
}
else if (o==3)
{
cin>>a>>b;
for (int k=a;k<=b;k++)
x=x^(1>>(k-1));
cout<<num<<endl;
}
else if (o==4)
{
cin>>a>>b;
for (i=a;i<=b;i++)
x=x&(~(1>>(i-1)));
cout<<num<<endl;
}
}
return 0;
}
50分暴力代码如下:
#include<iostream>
using namespace std;
int y[70];
char x;
int n,m,a,b;
int main(){
cin>>n>>m;
for (int i=1;i<=n;i++)
{
cin>>x;
y[i]=x-'0';
}
for (int i=1;i<=m;i++){
int o,count=0;
cin>>o;
/*
o=0:数y的0的个数;
o=1:数y的1的个数;
o=2:将a到b位取反;
o=3:将a到b位=1;
o=4:将a到b位=0;
*/
if (o==0)//数y的0的个数;
{
for (int i=1;i<=n;i++)
{
if(y[i]==0) count++;
}
cout<<count<<endl;
}
else if (o==1) //数y的1的个数;
{
for (int i=1;i<=n;i++)
{
if(y[i]==1) count++;
}
cout<<count<<endl;
}
else if (o==2)//将a到b位取反;
{
cin>>a>>b;
for (int i=a;i<=b;i++)
{
if(y[i]==1) y[i]=0;
else y[i]=1;
}
}
else if (o==3)//将a到b位=1;
{
cin>>a>>b;
for (int i=a;i<=b;i++)
{
y[i]=1;
}
}
else if (o==4)//将a到b位=0;
{
cin>>a>>b;
for (int i=a;i<=b;i++)
{
y[i]=0;
}
}
}
return 0;
}
上面50分代码,输入输出改成printf和scanf就可以AC了。
另外,经过一个晚上的努力,学会了位运算,可是,为啥只有50分呢?求数据啊,哭。
50分位运算代码如下:
#include<stdio.h>
using namespace std;
#define LL long long int
int NumberOfOne(LL x)
{
int count=0;
while (x)
{
count++;
x=x&x-1;//去掉最右边的1
}
return count;
}
int main()
{
int n,m,a,b,p,z=1;
LL x=0,y=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%1d",&p);
if (p) x=x|(z<<(n-i));
}//二进制表示状态,第一盏灯在最左边 。
for (int i=1;i<=m;i++)
{
int o,count=0;
scanf("%d",&o);
if (o==0)//数y的0的个数;
{
printf("%ld\n",n-NumberOfOne(x));
}
else if (o==1) //数y的1的个数;
{
printf("%ld\n",NumberOfOne(x));
}
else if (o==2)//将a到b位取反;
{
scanf("%d%d",&a,&b);
y=((z<<n-a+1)-1)-((z<<n-b)-1);//y的第a到b位置为1,其他位置为0
x=x^y;
}
else if (o==3)//将a到b位=1;
{
scanf("%d%d",&a,&b);
y=((z<<n-a+1)-1)-((z<<n-b)-1);
x=x|y;
}
else if (o==4)//将a到b位=0;
{
scanf("%d%d",&a,&b);
y=((z<<n-a+1)-1)-((z<<n-b)-1);
x=x&~y;
}
}
return 0;
}
总结这题做题过程中遇到的问题:
一、输入输出问题:
1、scanf居然可以指定读入整数的位数。scanf(“%1d”,&p)
2、scanf读入字符串或字符时,要注意对回车的处理,这里被卡了半小时
3、scanf输入要用&,输出不用,娃傻傻分不清
二、本题位运算几个点:
1、异或的定义,相同为0,不同为1,可以取反,可以用来加密,一个数与另一个数异或两次后为自己本身
2、x=x&x-1;//去掉最右边的1
3、y=((z<<n-a+1)-1)-((z<<n-b)-1);//y的第a到b位置为1,其他位置为0 (感觉只拿50分,最大可能这里错了,有可能数据溢出?或者对负数的处理存在问题?长路漫漫,继续研究)
4、取反是和y异或,全1是和y或,全0是y取反后再与,这个就很简单的事了
我想要测试数据啊啊啊。。。
最后的最后,娃发现,Z要定义为LL,果然改了后就AC了。
树状数组据说也可以做,不过呢,用高级数据结构前,还是先写个暴力吧,哪怕对拍也好。不过树状数组,不适合这题吧。
参考博文:https://blog.csdn.net/qq_40980489/article/details/81291494