C、C++自学笔记

89 阅读5分钟

C、C++从入门到精(ru)通(tu)

面向对象

对象存储空间
  • 空对象占用 1 字节
  • C++对象的成员属性和成员函数分开存放
  • 成员函数不属于类对象
  • 静态成员变量不属于类对象
  • 只有非静态成员变量属于类对象

面向过程

埃氏筛

判断素数 1 不是素数

将 2 到nn范围内的数写下来,2是最小的素数,将表中所有 2 的倍数划去,剩下的最小的数为3,其不能被更小的数整除,所以 3 是素数,再将表中所有3的倍数划去···以此类推,如果表中剩余的最小的数mm,那么mm就是素数,再将mm的倍数划去,反复操作依次枚举nn以内的素数。

时间复杂度: O(nloglogn)O(nloglogn)

通用预处理:

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;
快速幂

bpmodkb^pmodk

#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;
}
快速乘

abmodka*bmodk

#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
在最后加一个 10100 -> 1001x << 1 + 1
将最后一位变为 10100 -> 0101x I 1
乘法逆元

可以理解为找模倒数

an1(modb)an≡1(mod b) , 称 nnamodbamodb 的逆元,记作 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……

满足递推式:h(n)=h(0)h(n1)+h(1)h(n2)++h(n1)h(0)h(n)=h(0)h(n-1)+h(1)h(n-2)+……+h(n-1)h(0) ( h(0)=1,h(1)=1,n>=2h(0)=1,h(1)=1,n>=2 )

例题:www.luogu.com.cn/problem/P10…

并查集

处理一些不相交集合的合并问题

  • 初始化——用一个数组来存储每个元素的父节点,初始自己为父节点
int fa[MAXN];
void init(int n){
    for(int i=1;i<=n;++i)
        fa[i]=i;
}
  • 查询——找到 i 的祖先直接返回,可路径压缩优化查找

image.png

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;