模板索引:基本处理知识
手写整型快读
inline long long read(){
char readch=getchar(); ll readtmp=0;
ll readflag=1;
while(readch<'0' || '9'<readch){if(readch=='-')readflag=-1;readch=getchar();}
while('0'<=readch && readch<='9'){readtmp=readtmp*10+readch-'0';readch=getchar();}
return readtmp*readflag;
}
线性筛
void pre(){
for(int i = 2; i < maxm; i++){
if(!vis[i]) prime[++tot] = i;
for(int j = 1; j <= tot && i * prime[j] < maxm; j++){
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
快速幂
ll work(ll x,ll y,ll m){
if(y == 1) return x;
if(y == 0) return 1;
ll tmp = work(x, y/2, m)%m;
if(y % 2 == 1) return tmp * tmp %m *x %m;
else return tmp * tmp%m;
}
二分
while(r>l){
int mid = (l+r)/2;
if(a[mid]>=t)r = mid;
else l = mid+1;
}
while(l<r){
int mid = (l+r)/2;
if(check(mid) > m) r = mid;
else l = mid+1, ans = mid;
}
return ans;
离散化
- 去重函数
std::unique(a.begin(), a.end())用于将有序的数组 a 去重,返回去重后的最后一个数的迭代器,值得注意的是,去重只是把重复的数扔到这个迭代器后面,数组长度没有变。
-如果只想保留互异性的集合,可以.erase(unique(a.begin(), a.end()), a.end()) - 如何求出a里有多少个不同的数字?
std::unique(a.begin(), a.end())- a.begin() std::lower_bound(a.begin(), a.end(), x)用于在有序数列 a 里寻找第一个大于等于x的数的指针(地址),返回值为该数的指针。如果这个数不存在,则返回a.end()。
对于一个严格单调递增的数列(里面没有重复的数字),对于其中的一个数字x,显然std::lower_bound(a.begin(), a.end(), x)- a.begin() + 1就是 x 的排名。
位运算
一些约定
- 最右边一位是最低位, 是第 0 位。
- 最左边一位是最高位, 是第 \(n-1\) 位。(对 \(n\) 位二进制数)
- 默认做操作的两个二进制数位数相同。用 \(x_{i}\) 表示一个二进制数 \(x\) 的第 \(i\) 位。
- 不讨论负数的位运算。在应用中也不要对负数进行按位操作。
x << y就是 \(x\times2^y\) 的值 (在 \(x\) 的最高 \(y\) 位均为 0 时)。
类似的可以得到,x >> y 是 \(\lfloor\frac x {2^y}\rfloor\) 的值。
应用上,我们常用 1 << i 来得到 \(2^i\) 的结果。特别的,当 \(i>30\) 时,应该写作 1ull << i。
- 给定一个二进制串x,如何把它的第 \(i\) 位修改为 1?
x |= 1 << i - 给定一个二进制串x,保证它第 \(i\) 位为1,如何把它修改为 0 ?
x ^= 1 << i - 给出两个集合x,y,和32个元素从0∼31编号。每个集合含有若干个元素。如何O(1) 求出两集合的交集、补集、对称差?
(对称差指的是只在两个集合之一出现的元素组成的集合。例如{1,2} 和 {2,3} 的对称差是 {1,3})
可以把一个长度为 \(n\) 的二进制串 \(x\) 看作一个集合,则 \(x_i=0\) 表示 \(i\) 号元素不在 \(x\) 表示的集合中,\(x_i=1\) 表示 \(i\) 号元素在 \(x\) 表示的集合中。
两个长度为 \(n\) 的二进制串 \(x,y\) 做按位与、或、异或依次对应求两集合的交、并、对称差。
位运算bitset
| 语句 | 作用 |
|---|---|
std::bitset<N> s |
创建一个长度为 N 的二进制串 s |
| s.set(i, 0/1) | 把第 i 位设为 0/1,不写设为 1 |
| s.test(i) | 返回 \(s_i\) 的值 |
| s.count() | 返回 s 中 1 的数量 |
| s.reset() | 把 s 清空为全 0 |
可以用 & | ^ << >> 完成五个对应的按位操作。按位取反操作需要调用 s.flip()。
在使用 std::bitset<N> 声明 bitset 时,N 必须是个常量,且为编译器常量。
(在64bit 机器上)bitset 的底层实现可以看作有 n/64 个unsigned long long 开成的数组,每次做位运算时把这个数组遍历一遍逐个做按位操作.
一次运算的时间复杂度可以写作O(n/w),这里w = 64。显然其空间复杂度也是O(n/w)。
二维差分前缀和
P5542 [USACO19FEB] Painting The Barn S
注意题目给的是面积不是点的个数,应该取x1++, x2++
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3+10;
int n, k, ans;
int mp[maxn][maxn];
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for(int i = 1; i <= n; i++){
int Ax, Ay, Bx, By;
cin >> Ax >> Ay >> Bx >> By;
Ax++; Ay++;
mp[Ax][Ay]++;
mp[Bx+1][By+1]++;
mp[Ax][By+1]--;
mp[Bx+1][Ay]--;
}
for(int i = 1; i <= 1000; i++)
for(int j = 1; j <= 1000; j++){
mp[i][j] += mp[i-1][j] + mp[i][j-1] - mp[i-1][j-1];
//cout << mp[i][j] << " \n"[j == 8];
if(mp[i][j] == k) ans++;
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号