HDU 6085 Rikka with Candies
之前不知道bitset这种结构,估计知道了,也想不起来用,看博客说STL里面没有区间操作,用的话会超时,要自己手写,这里我找了网上大牛手写的bitset好好解析了一番,解题思路的话就是由于只需要求出种数的奇偶性,容易发现奇偶性的变化和2进制中亦或的结果相同。于是想到利用位操作来进行优化。对于Bi ,可以不断的枚举区间 [kBi,(k+1)Bi−1] ,而这一段区间中值 kBi+d 对Bi 取模的结果为 d ,即该区间对Bi 取模结果为区间 [0,Bi−1] 。于是根据 Ai 的值将取放置到区间中的对应位置,再不断枚举区间 [kBi,(k+1)Bi−1] 和区间 [0,B1]做亦或操作。询问时只要查询对应位的01值即可。实现时发现bitset没有区间操作的功能,所有利用unsigned long long 手动实现了bitset。最后复杂度为 O(n264) 。
以上摘自大牛题解:
自己注释过的代码,帮助理解:
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define ULL unsigned long long const int maxn=5e4+50; struct Bitset { ULL bit[1600]; void reset() { memset(bit,0,sizeof bit); } void flip(int pos)//把第pos位置一 { bit[pos>>6]^=1llu<<(pos&63); } bool judge(int pos)//判断第pos位是否是1 { return bit[pos>>6]&(1llu<<(pos&63)); } }A,ans; int b[maxn]; ULL pre[66]; int main() { freopen("input.txt","r",stdin); int T,n,m,q,a,mx; scanf("%d",&T); pre[0]=1; for(int i=1;i<64;i++) pre[i]=pre[i-1]|(1ll<<i); while(T--) { scanf("%d%d%d",&n,&m,&q); A.reset(); ans.reset(); mx=0;//最大的ax,是数据处理的范围 for(int i=0;i<n;i++) { scanf("%d",&a); A.flip(a); mx=max(a,mx); } for(int i=0;i<m;i++) { scanf("%d",&b[i]); for(int j=0;j*b[i]<=mx;j++) { int len=j*b[i]; int idx1=len>>6,p1=len&63; len+=b[i]-1; int idx2=len>>6,p2=len&63; if(idx1==idx2)//在同一组64位bit里面 { ULL tmp=A.bit[idx1]>>p1; tmp&=pre[p2-p1];//高位清零 ans.bit[0]^=tmp;//得出其中一组b区间的贡献 } else//b的异或区间跨越多个64位组 { int curid=0,curpos=0;//ans的异或起始区间 ULL tmp=A.bit[idx1]>>p1; tmp&=pre[63-p1];//从P1到本组最高位都要操作 ans.bit[0]^=tmp; curpos=63-p1;//已经操作至第curpos位 for(int k=idx1+1;k<idx2;k++) { if(curpos==63)//说明p1是从第0位操作的,那么接下来的所有整组可以直接操作 { ans.bit[++curid]^=A.bit[k]; } else//p1不是从第0位操作的,那么就是半组操作 { tmp=A.bit[k]<<(curpos+1);//补答案bit中的高位 ans.bit[curid++]^=tmp; tmp=A.bit[k]>>p1;//继续操作补完高位后剩下的低位 tmp&=pre[curpos]; ans.bit[curid]^=tmp;//答案bit中新的一组又操作了pos位 } } if(curpos==63)//相当于p1等于0了 { curid++; tmp=A.bit[idx2]&pre[p2]; ans.bit[curid]^=tmp; } else { if(p2<p1)//直接补上最后p2个,因为 { tmp=A.bit[idx2]&pre[p2]; tmp<<=curpos+1;//左移之后,把最后的p2位来补高位,同上 ans.bit[curid]^=tmp; } else//p2比较大的时候,要分成两次,第一次补高位,加下来操作剩余位 { tmp=A.bit[idx2]<<(curpos+1); ans.bit[curid++]^=tmp; tmp=A.bit[idx2]>>p1; tmp&=pre[p2-p1]; ans.bit[curid]^=tmp; } } } } } while(q--) { int k; scanf("%d",&k); if(ans.judge(k)) printf("1\n"); else printf("0\n"); } } return 0; }