trick
整理各种实(wai)用(men)技(xie)巧(dao)
光速幂
对于形如 \(a^b mod\ p\) 的柿子,常见的处理方法是快速幂 \(O(0)-O(\log b)\)(预处理-询问)。
如果某些题目要求单次询问 \(O(1)\),这时候就可以请出光速幂 \(O(\sqrt n)-O(1)\),但是注意光速幂要求底数和模数都固定,所以应用不广。
具体而言,我们将幂指数分成 \(k\) 块,预处理出 \(a^s,a^{2s}...,a^{ks}\) 和
\(a,a^2...a^{s-1}\),这样就有
\[a^b = a^{\lfloor\frac{b}{s}\rfloor \times s+{b\ mod\ s}} \mod p
\]
如果 \(p\) 是素数直接预处理到 \(p-1\) 即可。(或者到 \(φ(p)\),但没必要)。
code:
点击查看代码
void predeal(int x){
sq = sqrt(mod);
ph[0][0] = ph[0][1] = 1;
for(int i = 1; i <= sq; i++)
ph[i][0] = ph[i-1][0] * x % mod;
ph[1][1] = ph[sq][0];
for(int i = 2; i <= sq; i++)
ph[i][1] = ph[i-1][1] * ph[1][1] % mod;
return;
}
int power(int x){return ph[x/sq][1] * ph[x%sq][0] % mod;}
分治 popcount:
可以用 __builtin_popcount,但是这里介绍一种神奇的“分治算法”。
考虑把一个二进制数 \(x\) 两位一组分组,那么这两位的结果就是两位相加,类似于 FFT 中的蝶形优化,我们把这两位的答案存在他的相应位置上。
具体的,我们用 x 与上 \(01010101_2\) 得到一半答案,再把 x 右移一位,继续算另一半答案,最后加起来即可。
code:
const unsigned int bit1 = 0x55555555; // 01010101
const unsigned int bit2 = 0x33333333; // 00110011
const unsigned int bit4 = 0x0f0f0f0f; // 00001111
const unsigned int bit8 = 0x00ff00ff; // 同上
const unsigned int bit16 = 0x0000ffff;
int popcount(int x){
x = (x & bit1) + ((x >> 1) & bit1);
x = (x & bit2) + ((x >> 2) & bit2);
x = (x & bit4) + ((x >> 4) & bit4);
x = (x & bit8) + ((x >> 8) & bit8);
x = (x & bit16) + ((x >> 16) & bit16);
return x;
}
基数排序
这是个非常玄学的东西,甚至加入优化后能 \(5s\) 排 \(n = 1e8\) 的数列。
每次按当前最低位插入一个桶里,之后从桶里从低到高依次取出,继续排下一位。
int n, p10[MAXN];
int a[MAXN], b[MAXN];
vector<int> t[MAXN];
void Sort( ){
int mx = 0, mxbit = 0;
for(int i = 1; i <= n; i++) mx = max(mx, a[i]);
p10[0] = 1;
for(int i = 1; i <= 9; i++) p10[i] = p10[i-1] * 10;
while(mx){mxbit++; mx /= 10;}
for(int tt = 1; tt <= mxbit; tt++){
for(int i = 1; i <= n; i++) t[(a[i] / p10[tt-1]) % 10].push_back(a[i]);
int cur = 0;
for(int i = 0; i < 10; i++){
for(int j : t[i]) a[++cur] = j;
t[i].clear( );
}
}
}
bitset
玄学++,不少卡常题都能用 bitset 爆碾。
基本食用方法参考 oiwiki
这里介绍一种模拟 bitset 的方法,例题是:
P5539【XR-3】Unknown Mother-Goose