这道题的题意十分浅显易懂。
有一串很长很长不知道有多长(最长十万)的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;
}
貌似这个暴力比树状数组还快……
应该是数据太水了。
否则应该可以卡的。