个人C++随笔

  • 代码加速语句:

ios_base::sync_with_stdio(0);

cin.tie(0), cout.tie(0);

  • 关于ASCII

ASCLL码中的 可见字符 中最小的是空格(32)最大的是~(127)

常见的:A(65) a(97) 0(48)~9(57)

  • 小知识

  1. n&1; 判断是否为奇数;

  1. a<<=1------a*2; a>>=n;------a/(2^n);

  1. 指数exp(double x)

  1. 幂函数pow(double x,double y)

  1. 平方根sqrt 立方根cbrt

  1. #define inf (ll)1000000000000000000//long long

  1. 判断是否为闰年:year%4==0&&year%100!=0||year%400==0

  1. cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;

可以输出代码运行使用时间

  1. 1TB = 1024MB 1MB = 1024KB 1KB = 1024 Byte(字节) 1Byte = 8Bit(位)

一秒钟等于1000ms

  1. C语言反转字符串 strrev(s); reverse(s.begin(),s.end());

  1. to_string:作用是把数值类型如int、double、long等转化为string

区别是stoi的形参是const string*,

而atoi的形参是const char*。

c_str()的作用是将const string*转化为const char*。

  • 数组快速填充:

memset函数:

按照字节填充某字符,一般memset只能用来填充char型数组,除了0和-1,其他的不能

memset(g,0x3f,sizeof(g));

if(dis[n]==0x3f3f3f3f) return -1;

第一个的0x3f 等于第二个中的 0x3f3f3f3f;//memset是按字节填充 int 4 个字节

//按照16进制算一下就知道了,这里的0x3f按照4字节填充就是0x3f3f3f3f,所有判断的时候才会写0x3f3f3f3f

fill函数:

可以赋值任何,fill(arr, arr + n, 要填入的内容);

  • 栈stack

stack: 后进先出 top()返回一个栈顶元素的引用; pop()弹出栈顶元素;push()将其压入栈顶

while (!my_stack.empty())

{

cout << my_stack.top() << endl;//访问栈顶元素

my_stack.pop(); //将栈顶元素弹栈

}

  • 队列queue

队列

队列是先进先出,栈是先进后出

只能在容器的末尾添加新元素,只能从头部移除元素。

queue: front()返回 queue 中第一个元素的引用

back()返回 queue 中最后一个元素的引用

pop()删除 queue 中的第一个元素。

用途:BFS中

优先队列

priority_queue<int,vector<int>,greater<int>> q;小根堆、优先级队列

每次都是贪心得取最小的

默认是less<int>每次都是贪心得取最大的

注意:虽然greater<int>是从大到小但是在这里的效果是先取到最小值,less<int>同理

http://c.biancheng.net/view/6987.html

指的就是先进队列的元素并不一定先出队列,而是优先级最大的元素最先出队列。

假设当前有一个 priority_queue 容器适配器,其制定的排序规则是按照元素值从大到小进行排序。

根据此规则,自然是 priority_queue 中值最大的元素的优先级最高。

priority_queue<int, deque<int>, greater<int> >q;//从小到大

例题:https://www.luogu.com.cn/problem/P1090

int z=q.top();q.pop();q.push(z+x);

  • deque

deque 容器:

输出:

for (auto i = d.begin(); i < d.end(); i++) {

cout << *i << " ";

}

  • set

set

set: insert()插入元素

for (auto iter = myset.begin(); iter != myset.end(); ++iter) {//遍历

cout << *iter << endl; //upper_bound(key)/bound_bound(key)

}

是一个内部自动有序且不含重复元素的容器。

set可以在需要去重复元素的情况大放异彩,节省时间,减少思维量。

set<类型名> 变量名;https://zhuanlan.zhihu.com/p/344558356

类型名可以是int、double、char、struct,也可以是STL容器:vector、set、queue。

set只能通过迭代器(iterator)访问:set<int>::iterator it;

set<char>::iterator it;

auto it;

这样,就得到了迭代器it,并且可以通过*it来访问set里的元素。

for (auto iter = myset.begin(); iter != myset.end(); ++iter) {

cout << *iter << endl;}

typedef pair<int,string> p;

struct cmp1{

bool operator () ( const p &a , const p &b ) const {

return a.first>=b.first;

}

};

set<p,cmp1> st;//从大到小排序

for (auto iter = st.begin(); iter != st.end(); ++iter){

cout <<(*iter).second<<" "<<(*iter).first;

}

multiset

multiset 容器和 set 容器唯一的差别在于,multiset 容器允许存储多个值相同的元素,

而 set 容器中只能存储互不相同的元素。

访问vector<int,int> vec方法:

for(auto i : vec)ans = ans*(i.second + 1) % mod;

vector<int> vec:

for (int i = 0; i < vec.size(); i++) {

cout << vec[i] << " ";

}

插入元素:push_back(x);

  • map

map: for (auto iter = mp.begin(); iter != mp.end(); ++iter) {

cout << iter->first << " " << iter->second << endl;

}

map<string, int>mp;

如果要根据map容器中的元素进行排序,可以创建一个vector,先将元素push_back()进vector

再使用sort(vec.begin(),vec.end(),cmp);https://www.acwing.com/problem/content/1802/

map<string,vector<int>> a;https://ac.nowcoder.com/acm/contest/31822/D

a[s].size()!=0a[s].push_back(i);

https://ac.nowcoder.com/acm/contest/32297/C

map<string,int> mp

for(auto i=mp.begin();i!=mp.end();i++){

int m=i->second;//不能是i.second;

}

map:默认按照key升序排序map<key, value>

//根据value来排序

typedef pair<key, value> pp;

bool comp(pp i, pp j) { return i.second < j.second; }

sort(scoreVector.begin(), scoreVector.end(), cmp()); //需要指定cmp

//以下为根据key来排序

map<string, int, greater<string> > scoreMap;

struct cmp //自定义比较规则{

bool operator() (const string& str1, const string& str2){

return str1.length() < str2.length();

}

}

map<string, int, cmp > scoreMap; //这边调用cmp

map<pair<int,int>,int>pre;

pair<int,int>temp; 的组合使用

https://blog.csdn.net/dtwd886/article/details/107084333

  • vector

vector: push_back():插入元素 pop_back():删除 vector 容器中最后一个元素

insert():在指定的位置插入一个或多个元素。

for (int i = 0; i < values.size(); i++) {

cout << values[i] << " ";

}

for(int i=0;i<vec.size();i++){

if(vec[i]>x){

vec.insert(vec.begin()+i,x);

break;

}

}

  • 整行读取

getline 的 C++ 函数。此函数可读取整行,包括前导和嵌入的空格,并将其存储在字符串对象中。

如:string s; getline(cin,s); 注意前面加一个getchar();

  • 最大公约数、最小公倍数

最大公因数:gcd

inline int gcd(int a,int b) {

return b>0 ? gcd(b,a%b):a;

}

最小公倍数:lcm

两数的乘积除以最大公约数就得到最小公倍数

int lcm(int a,int b){

return a*b/gcd(a,b);

}

C++库里有个函数_ _gcd(a,b)这个函数,可以用来求a与b的最大公因数

扩展欧几里得算法:exgcd

int exgcd(int a,int b,int &x,int &y){

if(!b){x=1;y=0;return a;}

int d=exgcd(b,a%b,y,x);

y-=a/b*x;return d;

}

  • 全排列:next_permutation

next_permutation()可以按照字典序实现全排列,包含于头文件<algorithm>

例如:数组{1,2,3}:按照字典序全排列为:123,132,213,231,312,321

例如:字符串adc:按照字典序全排列为:abc,acb,bac,bca,cab,cba

do{

for(int i=1;i<=n;i++){

if(i==1) cout<<q[i];

else cout<<" "<<q[i];

}

cout<<endl;

}while(next_permutation(a,a+n));

例题:https://ac.nowcoder.com/acm/contest/11217/I

  • lowbit

lowbit()函数用来取一个二进制最低位的一与后边的0组成的数

例:5(101),lowbit(5)=1(1)

12(1100),lowbit(12)=4(100)

int lowbit(int x){

return x&(-x);

}

  • substr

substr有2种用法:

假设:string s = "0123456789";

string sub1 = s.substr(5); //只有一个数字5表示从下标为5开始一直到结尾:sub1 = "56789"

string sub2 = s.substr(5, 3); //从下标为5开始截取长度为3位:sub2 = "567"

  • binary_search

binary_search() 函数定义在<algorithm>头文件中,用于查找指定区域内是否包含某个目标元素。

该函数会返回一个 bool 类型值,如果 binary_search() 函数在 [first, last) 区域内成功找到

和 val 相等的元素,则返回 true;反之则返回 false。//binary_search(a, a + 9, 4);

  • lower_bound、upper_bound

lower_bound() 函数用于在指定区域内查找不小于(大于等于)目标值的第一个元素

upper_bound() 函数用于在指定范围内查找大于目标值的第一个元素

//lower_bound(a, a+n, x)-a;如果数组中存在x

lower_bound(a,a+n,x)-a //下标从0开始

lower_bound(a+1,a+n+1,x)-a //下标从1开始

它们就能取得最小的aa数组的下标i,满足ai>=x

//upper_bound(a, a+n, x)-a;如果数组中不存在x

(upper_bound(a+1,a+N+1,a[i]+C)-a)-(lower_bound(a+1,a+N+1,a[i]+C)-a)

可以找到某一个值的个数

  • greater、less

greater<int>() 内置类型的由大到小排序

less<int>()) 内置类型的由小到大排序

常用于sort函数 sort(a ,a + len, greater<int>());

sort(a, a + len, less<int>());

  • 最长不上升子序列的长度

最长不上升子序列的长度

int len1=1;

int a=0;

dp1[len1]=arr[1];

for(int i=2;i<=n;i++){

if(arr[i]<=dp1[len1]){

dp1[++len1]=arr[i];

}

else{

int p=upper_bound(dp1+1,dp1+1+len1,arr[i],greater<int>())-dp1;

dp1[p]=arr[i];

}

}

cout<<len1<<endl;

  • 最长上升子序列的长度

最长上升子序列的长度

int len2=1;

dp2[len2]=arr[1];

for(int i=2;i<=n;i++){

if(arr[i]>dp2[len2]){

dp2[++len2]=arr[i];

}

else{

int p=lower_bound(dp2+1,dp2+1+len2,arr[i])-dp2;

dp2[p]=arr[i];

}

}

cout<<len2<<endl;

  • 质数(素数)判断

判断是否为质数

void IsPrime(int x){

if (1 == x){

cout << "1既不是质数也不是合数!" << endl;

return;

}

for (int i = 2; i <= sqrt(x); i++)或者i<=x / i; //注意用i * i <= x可能会超时

if (x%i == 0){

cout << "您所输入的数字为合数!" << endl;

return;

}

cout << "您所输入的数字为质数!" << endl;

return;

}

bool isprime(int n){//判断是否质数

int s=sqrt(double(n));

for(int i=2;i<=s;i++){

if(n%i==0)return false;

}

return true;

}

  • 线性筛法

线性筛法:就是在线性时间内(也就是O(n))用筛选的方法把素数找出 来的一种算法

https://www.cnblogs.com/Miroerwf/p/7776390.html

for (int i = 2; i < MAXL; ++i){

if (!check[i]){

prime[tot++] = i;

}

for (int j = 0; j < tot; ++j){或者for(int j=0;prime[ j ]<=MAXL/i;j++){

if (i * prime[j] > MAXL) break;

check[i*prime[j]] = 1;

if (i % prime[j] == 0) break;

}

  • 质因数、约数

约数,又称因数。整数a除以整数b(b≠0) 除得的商正好是整数而没有余数,

我们就说a能被b整除,或b能整除a。a称为b的倍数,b称为a的约数。

关于质因数:

int x;cin>>x;

for(int j=2;j<=x/j;j++){

if(x%j==0){

while(x%j==0){

mp[j]++;x/=j;

}

}

}

if(x>1) mp[x]++;

如果 N=p1^c1∗p2^c2∗…∗pk^ck

约数个数:(c1+1)∗(c2+1)∗…∗(ck+1)

for(auto i : mp) ans = ans*(i.second + 1) % mod;

约数之和: (p1^0+p1^1+…+p1^c1)∗…∗(pk0+pk1+…+pkck)

a = i.first; b = i.second;

while (b -- ) t = (t * a + 1) % mod;

ans=ans*t%mod;

45、欧拉公式:在1~N中与N互质的数的个数被称为欧拉函数,记为ϕ(N)。

若在算数基本定理中,N=p1^α1*p2^α2⋅⋅⋅pk^αk则:

ϕ(N)=N∗(p1−1/p1)∗(p2−1/p2)∗…∗(pk−1/pk)

https://www.acwing.com/problem/content/875/

  • 背包问题

背包问题:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);

背包问题变形:dp[i][j]=max(dp[i-1][j],dp[i-1][(j+a[i][0]%k)%k]+a[i][1]);

https://ac.nowcoder.com/acm/contest/23479/I

01背包问题从体积大到体积小考虑for(int j=m;j>=v[i];j--)

完全背包问题从体积小到体积大考虑for(int j=v[i];j<=m;j++)

如果物品个数有限,应该从大到小,保证这样可以保证物品不会重复放入;

如果物品个数无限,应该从小到大;

举一个例子:物品0的重量weight[0] = 1,价值value[0] = 15

如果正序遍历

dp[1] = dp[1 - weight[0]] + value[0] = 15

dp[2] = dp[2 - weight[0]] + value[0] = 30

此时dp[2]就已经是30了,意味着物品0,被放入了两次,所以不能正序遍历。

倒序就是先算dp[2]

dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)

dp[1] = dp[1 - weight[0]] + value[0] = 15

所以从后往前循环,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了。

  • 快速幂

快速幂:

while(b){

if (b&1)ans=(ans*a)%p;

b>>=1;

a=(a*a)%p;

}

  • 前缀和

前缀和是指,对于新数组 sumsum,sum[i]sum[i] 代表a数组中前i项之和。

sumsum 数组可以通过 sum[i]=sum[i-1]+a[i]sum[i]=sum[i−1]+a[i] 得出

例:https://ac.nowcoder.com/acm/contest/23479/C

  • 逆元

求逆元:

inline long long fast(long long x){//求x在%mod意义下的逆元

int y=mod-2;

long long res=1;

while(y){

if(y&1)res=res*x%mod;

x=x*x%mod;

y>>=1;

}

return res;

}

快速幂求逆元

快速幂求逆元:若整数 b,m 互质,并且对于任意的整数 a,如果满足 b|a,则存在一个整数 x,

使得 a/b≡a×x(modm),则称 x 为 b 的模 m 乘法逆元,记为 b−1(modm)。

b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时,bm−2 即为 b 的乘法逆元。

if(a%p==0) cout<<"impossible"<<endl;

else cout<<fun(a,p-2,p)<<endl;//快速幂

https://www.acwing.com/problem/content/878/

  • 卡特兰数

卡特兰数是组合数学中一个常在各种计数问题中出现的数列。

https://baike.sogou.com/v9693847.htm?fromTitle=%E5%8D%A1%E7%89%B9%E5%85%B0%E6%95%B0&ch=frombaikevr

1:令h(0)=1,h(1)=1,catalan数满足递推式:

h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (n>=2)

2:h(n)=h(n-1)*(4*n-2)/(n+1);

3:h(n)=C(n,2n)*(2n-1) (n=0,1,2,...)/h(n)=c(2n,n)-c(2n,n+1)(n=0,1,2,...)

问题:1: 一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?

凸多边形三角划分

给定节点组成二叉树:给定N个节点,能构成多少种不同的二叉树?

例题:https://www.luogu.com.cn/record/71559353

  • 最近公共祖先问题LCA

LCA问题(Least Common Ancestors,最近公共祖先问题),是指给定一棵有根树T,

给出若干个查询LCA(u, v)(通常查询数量较大),每次求树T中两个顶点u和v的最近公共祖先,

即找一个节点,同时是u和v的祖先,并且深度尽可能大(尽可能远离树根)。

https://www.luogu.com.cn/problem/P3884

LCA问题有很多解法:线段树、Tarjan算法、跳表、RMQ与LCA互相转化等。

  • 树状数组

树状数组的使用http://acm.hdu.edu.cn/showproblem.php?pid=1166

详细说明:https://blog.csdn.net/bestsort/article/details/80796531

int lowbit(int x){

return x&(-x);

}

void updata(int x,int y,int n){ //单点更新:

for(int i=x;i<=n;i+=lowbit(i)) //x为更新的位置,y为更新后的数,n为数组最大值

a[i]+=y;

}

int getsum(int x){ //区间查询

int ans=0;

for(int i=x;i;i-=lowbit(i))

ans+=a[i];

return ans;

}

  • 向量平行问题

判断两个向量相加得到一个向量与目标向量平行:

(a[i].X2 - a[i].X1 + a[j].X2 - a[j].X1) * (Y2 - Y1) == (a[i].Y2 - a[i].Y1 + a[j].Y2 - a[j].Y1) * (X2 - X1)

注a[i].X2 - a[i].X1 + a[j].X2 - a[j].X1=X2 - X1;a[i].Y2 - a[i].Y1 + a[j].Y2 - a[j].Y1=Y2-Y1;

  • 平面划分问题

平面划分:我们记第一条直线的权值总为1,所以平面个数即第一条直线权值1加第二条直线权值2再加1。

https://edu.csdn.net/skill/algorithm/algorithm-03f5a1063e9948dab7d89631695b4323?category=192

  • 邻接表

int h[N], e[N], ne[N], idx; //邻接表

void insert(int x) {

// c++中如果是负数 那他取模也是负的 所以 加N 再 %N 就一定是一个正数

int k = (x % N + N) % N;

e[idx] = x;

ne[idx] = h[k];

h[k] = idx++;

}

for (int i = h[k]; i != -1; i = ne[i]) {

if (e[i] == x) {

return true;

}

}

return false;

  • 并查集

int find(int x){

if(x!=q[x])

q[x]=find(q[x]);

return q[x];

}

例:AcWing 837. 连通块中点的数量 - AcWing

  • 链表

单链表

//e[N]存放结点的val,ne[N]存放next指向的下标

//idx表示当前已使到的点的下标,head表示头结点

int e[N],ne[N],idx,head;

void init(){//初始化

head=-1;idx=0;

}

void add_to_head(int x){//插入到头结点的后面

e[idx]=x;

ne[idx]=head;

head=idx;

idx++;

}

void add(int k,int x){//在第k位置的后面插入一个x

e[idx]=x;

ne[idx]=ne[k];

ne[k]=idx;

idx++;

}

void remove(int k){//删除第k位置的后面一个结点

ne[k]=ne[ne[k]];

}

for(int i=head;i!=-1;i=ne[i])//遍历

cout<<e[i]<<" ";

cout<<endl;

双链表

//r[N]表示从左往右指向

//l[N]表示从右往左指向

int e[N],l[N],r[N],idx;

void init(){//初始化

r[0]=1;//1表示最右边的结点的下标

l[1]=0;//0表示最左边的结点的下标

idx=2;

}

void add(int k,int x){//在k位置的右边插入x

e[idx]=x;

l[idx]=k;

r[idx]=r[k];

l[r[k]]=idx;

r[k]=idx;

idx++;

}

(同样适用于最左边、最右边的插入)

(最左边:add(0,x); 最右边:add(l[1],x);)

void remove(int k){//删除k位置的结点

r[l[k]]=r[k];

l[r[k]]=l[k];

}

for(int i=r[0];i!=1;i=r[i])//遍历

cout<<e[i]<<" ";

cout<<endl;

  • 哈希表

最大的数据范围N尽量取大于数据范围的第一个素数,这样可以使得映射到N范围内的任意一个数的数量更少

h[N]相当于单链表中的head,因为这里不仅仅只有一个head,每一个插槽都可以有一个链表,对应一个head

初始化:memset(h,-1,sizeof(h));//初始化,每一个链表的head指针初始为-1

int h[N],e[N],ne[N],idx;

void insert(int x){//插入操作

int k=(x % N + N) % N;//负数取mod得负数,加一个N将负数转化为正数,再取mod将结果限制在N范围内

e[idx]=x;

ne[idx]=h[k];

h[k]=idx;

idx++;

}

bool find(int x){//查询操作

int k=(x % N + N) % N;

for(int i=h[k];i!=-1;i=ne[i])

if(e[i]==x) return true;

return false;

}

  • 高精度

高精度加法

//只能是两个正数相加

string add(string str1,string str2){//高精度加法

string str;

int len1=str1.length();

int len2=str2.length();

//前面补0,弄成长度相同

if(len1<len2){

for(int i=1;i<=len2-len1;i++)

str1="0"+str1;

}

else{

for(int i=1;i<=len1-len2;i++)

str2="0"+str2;

}

len1=str1.length();

int cf=0;

int temp;

for(int i=len1-1;i>=0;i--){

temp=str1[i]-'0'+str2[i]-'0'+cf;

cf=temp/10;

temp%=10;

str=char(temp+'0')+str;

}

if(cf!=0) str=char(cf+'0')+str;

return str;

}

高精度乘法

793. 高精度乘法 - AcWing题库

#include<bits/stdc++.h>

using namespace std;

string s1,s2;

int a[100005],b[100005],c[100005];

int main(){

cin>>s1>>s2;

a[0]=s1.length();

b[0]=s2.length();

c[0]=a[0]+b[0]-1;

for(int i=1;i<=a[0];i++)

a[i]=s1[a[0]-i]-'0';

for(int i=1;i<=b[0];i++)

b[i]=s2[b[0]-i]-'0';

for(int i=1;i<=a[0];i++){

for(int j=1;j<=b[0];j++){

c[i+j-1]+=a[i]*b[j];

c[i+j]+=c[i+j-1]/10;

c[i+j-1]%=10;

}

}

if(c[c[0]+1]>=1) c[0]++;

while(c[c[0]]==0&&c[0]>1) c[0]--;

for(int i=c[0];i>=1;i--) cout<<c[i];

cout<<endl;

return 0;

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值