树状的灯

先上一篇位运算好博客: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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值