C、C++从入门到精(ru)通(tu)
面向对象
对象存储空间
- 空对象占用 1 字节
- C++对象的成员属性和成员函数分开存放
- 成员函数不属于类对象
- 静态成员变量不属于类对象
- 只有非静态成员变量属于类对象
面向过程
埃氏筛
判断素数 1 不是素数
将 2 到范围内的数写下来,2是最小的素数,将表中所有 2 的倍数划去,剩下的最小的数为3,其不能被更小的数整除,所以 3 是素数,再将表中所有3的倍数划去···以此类推,如果表中剩余的最小的数是,那么就是素数,再将的倍数划去,反复操作依次枚举以内的素数。
时间复杂度:
通用预处理:
bool isprime[maxn];
void sieve(){
for(int i=0;i<=maxn;++i) isprime[i]=true;
isprime[0]=isprime[1]=false;
for(int i=2;i<=maxn;++i)
if(isprime[i])
for(int j=2*i;j<=maxn;j+=i)
isprime[j]=false;
}
例:求 1~20 之间的素数个数
int cut=0;
bool isprime[21];
for(int i=0;i<=20;++i) isprime[i]=true; // 全部置为真
isprime[0]=isprime[1]=false; // 0,1不是素数
for(int i=2;i<=20;++i){
if(isprime[i]){
for(int j=2*i;j<=20;j+=i)
isprime[j]=false;
cnt++;
}
}
cout << cnt;
快速幂
求
#define ll long long
ll quick_power(ll b,ll p,ll k){
ll ans=1%k;
//b%=k; 预防乘法溢出
while(p){
if(p%2==1) // p&1
ans=ans*b%k;
b=b*b%k;
p/=2; // p>>=1
}
return ans;
}
快速乘
求
#define ll long long
ll quick_mul(ll a,ll b,ll k){
ll ans=0;
while(b){
if(b%2==1) // b&1
ans=(ans+a)%k;
a=(a+a)%k;
b/=2; // b>>=1
}
return ans;
}
位运算
(执行效率非常高)计算机中的数在内存中都是以二进制形式进行存储,位运算就是对内存中的二进制位直接进行操作,提升性能
运算符:& 与 、 | 或 、 ^ 异或-相同为 0,相异为 1 、 ~ 取反- 0 变 1, 1 变 0 、 << 左移-二进制全部左移若干位,高位丢弃,低位补0 、 >> 右移-二进制全部右移若干位,对无符号数高位补 0 ,有符号数右移补 0
操作示例:
| 操作 | 预期 | Code |
|---|---|---|
| 在最后加一个 1 | 0100 -> 1001 | x << 1 + 1 |
| 将最后一位变为 1 | 0100 -> 0101 | x I 1 |
乘法逆元
可以理解为找模倒数
, 称 为 的逆元,记作 a^-1 = n (’≡‘ 同余符号)
正如:( 7 / 2 ) mod 5 = ( 7 x 3 ) mod 5 ,那么可以得到 2 x 3 mod 5 = 1
a 的乘法逆元是 z = a^-1 , z = a^-1 的乘法逆元是 a , z^-1 = ( a^-1 ) ^-1 = a
注意:
1> 模 n 下互为乘法逆元,一般只考虑比 n 小的
2> a 在模 n 内的乘法逆元 a^-1 是唯一的
3> 存在条件: gcd( a,n ) = 1 互推 模 n 下,a 有乘法逆元
引入费马小定理: p 是一个质数,而整数 a 不是 p 的倍数,则 a^p-1 ≡ 1 ( mod p ),等价于 a·a^(p-2) ≡ 1 ( mod p ),a 的乘法逆元为 a^p-2
容斥原理
卡特兰数
前几项为:1,2,5,14,42,132,429,1430……
满足递推式: ( )
例题:www.luogu.com.cn/problem/P10…
并查集
处理一些不相交集合的合并问题
- 初始化——用一个数组来存储每个元素的父节点,初始自己为父节点
int fa[MAXN];
void init(int n){
for(int i=1;i<=n;++i)
fa[i]=i;
}
- 查询——找到 i 的祖先直接返回,可路径压缩优化查找
int find(int i){
if(fa[i]==i) // 递归出口,达到祖先位置返回祖先
return i;
else
return find(fa[i]);
}
// 路径压缩后->
int find(int i){
if(fa[i]!=i)
fa[i]=find(fa[i]);
return fa[i];
}
- 合并
void unionn(int i,int j){
int i_fa=find(i),j_fa=find(j);
fa[i_fa]=j_fa; // i祖先指向j的祖先
}
容器
函数
iota()
对一个范围数据进行赋值
iota(begin, end, n); // 将从 n 开始的递增数列赋给从 begin 开始到 end
next_permutation()
求全排列函数,要得到所有排列需优先排序
next_permutation(arr,arr+n); // 会取得[first,last)所标示的序列的下一个排列组合,如果没有下一个组合返回false,否则返回true
// 输出序列{1,2,3,4}全排列
a[1]={1,2,3,4};
do{
for(int i=0;i<4;++i)
cout << a[i] << " ";
cout << endl;
}while(next_permutation(a,a+4));
min_element()/max_element()
返回容器中最小值/最大值
max_element(first,end,cmp);
// 1>参数cmp可不写,默认将容器元素从小到大排序,max_element输出最后一个值,min输出第一个值
// 2>可用于vector等,也可用于int arr[4]或string str[4]
// 3>返回的是迭代器,输出值需要在前加'*'
accumulate()
numeric库中求指定范围内的元素和
accumulate(起始迭代器,结束迭代器,初始值,自定义操作函数)
// 共四个参数,前三个为必须
// 若不指定第四个参数,默认对范围内元素进行累加
nth_element()
STL库中求区间第K小的数
nth_element(arr,arr+k,arr+len); // 把下标为k的元素放在正确位置,对其他元素没有排序
ceil()/round()/floor()
分别为向上取整/四舍五入/向下取整
gcd()/lcm()
最大公约数/最小公倍数
两数乘积与两数最大公约数和最小公倍数乘积相等
gcd(a,b)*lcm(a,b)=a*b;