【codevs2343】简单题【位运算】【卡常大法好】

这道题的题意十分浅显易懂。
有一串很长很长不知道有多长(最长十万)的01序列,一开始全是0.

要你维护两种操作:将一个区间内的数翻转(就是1变0,0变1,就是异或1)、询问某一位是0还是1.

树状数组的裸题啊。

但是我使用了传说中的卡常数大法~~~

直接暴力修改,但是把64个01位压进了一个unsigned long long。

这样修改是O(n)的,但是常数奇小- -

不过位运算坑死我了。

~0ULL>>63是1,但是>>64又变成了~0ULL……

所以要把右一r+1位写成>>r>>1.

假设从低位到高位是从右往左数,从第0位算起(实际读入区间的时候要减1)。

因为序列长10 0000,所以我们开个1600的数组d好了。

由于某种画蛇添足的原因,我把数组下标的开始位置设成了1……

现在要把第L位到第R位翻转。

第L位一定在d[L/64+1]即d[(L>>6)+1],同理第R位在d[(R>>6)+1]。

然后L、R对64取模(&63)就得到他们在这个64位整数里的位置。

本来我们应该把d[(L>>6)+1]的第L&63位到第63位翻转,把d[((L>>6)+2)..((R>>6))]翻转,再把d[(R>>6)+1]的第0位到第R&63位翻转。

因为翻了两次等于没翻,所以我们可以先把d[(L>>6)+1]的第0位到第(L&63)-1位、d[(R>>6)+1]的第(R&63)+1位到第63位翻转,

再for i从(L>>6)+1到(R>>6)+1,这样就行了。

这玩意卡了我老长时间,看来我还有很长很长不知道有多长的路要走……

- -

/*
作者:ZMOIYNLP
题目:p2343 简单题
*/
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef unsigned long long ull;
struct hehe{
	ull d[1600];
	inline void flip(int l,int r){
		int l1=(l>>6)+1,r1=(r>>6)+1;
		l&=63,r&=63;
		d[l1]^=(~0ULL)>>(63-l)>>1;
		d[r1]^=(~0ULL>>r>>1)<<(r+1);
		for(int i=l1;i<=r1;++i)
			d[i]^=~0ULL;
	}
}a;
inline void read(int &x){
	x=0;
	char ch=getchar();
	if(ch==-1) return;
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	bool flag=false;
	if(ch=='-') flag=true,ch=getchar();
	do{x=x*10+ch-48;ch=getchar();}while(ch>='0'&&ch<='9');
	if(flag) x=-x;
}
int main(){
	int n,m,t,l,r;
	read(n);read(m);
	while(m--){
		read(t);
		if(t==1){
			read(l);read(r);
			a.flip(l-1,r-1);
		}
		else{
			read(l);
			putchar(((a.d[(l-1>>6)+1]>>(l-1))&1)+48);
			putchar('\n');
		}
	}
	return 0;
}

貌似这个暴力比树状数组还快……

应该是数据太水了。

否则应该可以卡的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值