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

来源:互联网 发布:雇佣网络水军 编辑:程序博客网 时间:2024/06/05 00:23

这道题的题意十分浅显易懂。
有一串很长很长不知道有多长(最长十万)的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
原创粉丝点击