模板索引:基本处理知识

手写整型快读

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 的排名

位运算

一些约定

  1. 最右边一位是最低位, 是第 0 位。
  2. 最左边一位是最高位, 是第 \(n-1\) 位。(对 \(n\) 位二进制数)
  3. 默认做操作的两个二进制数位数相同。用 \(x_{i}\) 表示一个二进制数 \(x\) 的第 \(i\) 位。
  4. 不讨论负数的位运算。在应用中也不要对负数进行按位操作。

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;
}
posted @ 2025-07-09 23:18  [丘李]Chilllee  阅读(15)  评论(1)    收藏  举报