一些基础/神奇的知识
1.摩尔投票法
可能一辈子用不到的东西
不占用空间统计一个出现次数大于等于总数一半的众数
扫一遍,记录当前众数和当前众数的出现次数。若次数为零记录当前数为众数,否则若当前数与当前众数相同计数$+1$,不同计数$-1$。因为这个众数出现了一半以上,最后计数下来一定会剩下它自己,其余的数都被“抵消”掉了
2.线性基
因为不知道放哪个模板下就放这里了23333
一种数据结构,它是一个大小为$log$ $x$的数组,对于每一位$bit$记录一个最高位$bit$为$1$的数,这个数组可以表示所有这些数的子集的异或运算,初始时数组全部为零。它能够在$log$ $x$的时间内取得一个数集中子集异或和最值,或是判断能否得到某个值,或是完成对一个数的插入。
插入一个数$x$时从高位到低位遍历$x$,判断:若数组的这一位还没有数,则改为$x$;否则用数组中的这个数异或$x$更新$x$。这样操作之后$x$要么被拆开记录到了数组中要么被忽略掉了,被忽略掉则说明此时这个数无法继续产生贡献。
查询最值时,显然最小值就是线性基中最小的非零数。最大值的查询是通过贪心的思想实现的:从高到低遍历数组,如果$maxx$与当前位的数异或变大则异或上当前数,即使使得后面的某位取不到$1$显然也是更优。
可不可以更厉害一点啊?
现在查询在异或某个数$x'$基础上的最值
然而这并不算更厉害了,你就把那个数当成ans扔进去异或就好了,原因就是线性基的原理。
可不可以更高级一点啊?
线性基能不能合并?
当然能,按位把那位上的数插进另一个线性基里就行了(这也不高级啊=。=
可不可以更高级厉害一点啊(什么玩意=。=
现在查询第$k$大,于是线性基展现了它完全的面目,它其实长这样:
1 int siz,zero;//一般不算零,所以记一下 2 void Insert(int x) 3 { 4 for(int i=60;~i;i--) 5 if(x&(1ll<<i)) 6 { 7 if(bas[i]) x^=bas[i]; 8 else 9 { 10 siz++,bas[i]=x; 11 for(int j=i-1;~j;j--) 12 if(bas[j]&&(bas[i]&(1ll<<j))) bas[i]^=bas[j]; 13 for(int j=60;j>i;j--) 14 if(bas[j]&(1ll<<i)) bas[j]^=bas[i]; 15 break; 16 } 17 } 18 sz=qpow(2,siz)-1; 19 } 20 int Query(int k) 21 { 22 if(zero) k--; 23 if(k>sz) return -1; 24 int res=0; 25 for(int i=0;i<=60;i++) 26 if(k&(1ll<<i)) res^=bas[i]; 27 return res; 28 }
emmm因为我暂时用不到,所以就放个代码算了=。=
底下的是一个相对可爱的线性基
1 #include<cstdio> 2 long long bas[64],rd,n,ans; 3 int main () 4 { 5 scanf("%lld",&n); 6 for(int i=1;i<=n;i++) 7 { 8 scanf("%lld",&rd); 9 for(int j=60;~j;j--) 10 if(rd&(1ll<<j)) 11 if(!bas[j]) {bas[j]=rd;break;} 12 else rd^=bas[j]; 13 } 14 for(int i=60;~i;i--) if(ans<(ans^bas[i])) ans^=bas[i]; 15 printf("%lld",ans); return 0; 16 }
3.位运算
将某个数的二进制表示的一段赋为$1$:构造一个遮罩,将这一段赋为$1$,其余赋为$0$,用原数或上这个遮罩
将某个数的二进制表示的一段赋为$0$:构造一个遮罩,将这一段赋为$0$,其余赋为$1$,用原数与上这个遮罩
将某个数的二进制表示的一段取反:构造一个遮罩,将这一段赋为$1$,其余赋为$0$,用原数异或上这个遮罩
对于一个数$x$
取末尾连续$1$:$x\&(x$ $xor(x+1))$
取最低位$1(lowbit)$:$x\&-x$
遍历所有的$1$:不断删$lowbit$
4.bitset
一个在处理与位运算有关的问题时很好用的东西,这里放一些函数
$[pos]$ 访问$pos$下标处的二进制位
$.count()$ 返回bitset中$1$的个数
$.size()$ 返回bitset的位数
$.set((pos))$ 将bitset所有位$(pos$这个位$)$置为$1$
$.reset((pos))$ 将bitset所有位$(pos$这个位$)$置为$0$
$.flip((pos))$ 将bitset所有位$(pos$这个位$)$取反
bitset神奇操作:小清新人渣的本愿
5.高维前缀和/差分
前缀和(求一个集合所有子集之和):
int all=(1<<n)-1; for(int i=1;i<=all;i<<=1) for(int j=0;j<=all;j++) if(i&j) sum[j]+=sum[i^j];
差分就把加改成减......
6.高阶差分
$△^k a_i=\sum\limits_{j=0}^k(-1)^jC_k^ja_{i-j}$
7.可删优先队列
开一个存被删掉元素的堆,两堆堆顶一样时先一起pop直到一堆为空或堆顶不一样为止

1 struct EPQ 2 { 3 priority_queue<int> hp,de; 4 void Push(int x){hp.push(x);} 5 void Erase(int x){de.push(x);} 6 int Size(){return hp.size()-de.size();} 7 int Top() 8 { 9 while(!de.empty()&&de.top()==hp.top()) 10 de.pop(),hp.pop(); 11 return hp.top(); 12 } 13 void Pop() 14 { 15 while(!de.empty()&&de.top()==hp.top()) 16 de.pop(),hp.pop(); 17 hp.pop(); 18 } 19 // int Sec() 20 // { 21 // int tp1=Top();Pop(); 22 // int tp2=Top();Push(tp1); 23 // return tp2; 24 // } 25 };