一些基础/神奇的知识

 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 };
View Code

 

posted @ 2018-09-18 17:45  Speranza_Leaf  阅读(187)  评论(0)    收藏  举报