代码加速语句:
ios_base::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
关于ASCII
ASCLL码中的 可见字符 中最小的是空格(32)最大的是~(127)
常见的:A(65) a(97) 0(48)~9(57)
小知识
n&1; 判断是否为奇数;
a<<=1------a*2; a>>=n;------a/(2^n);
指数exp(double x)
幂函数pow(double x,double y)
平方根sqrt 立方根cbrt
#define inf (ll)1000000000000000000//long long
判断是否为闰年:year%4==0&&year%100!=0||year%400==0
cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
可以输出代码运行使用时间
1TB = 1024MB 1MB = 1024KB 1KB = 1024 Byte(字节) 1Byte = 8Bit(位)
一秒钟等于1000ms
C语言反转字符串 strrev(s); reverse(s.begin(),s.end());
to_string:作用是把数值类型如int、double、long等转化为string
区别是stoi的形参是const string*,
而atoi的形参是const char*。
c_str()的作用是将const string*转化为const char*。
![](https://i-blog.csdnimg.cn/blog_migrate/5ea3cabbeb8459cef614f19c1e114685.png)
数组快速填充:
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中
![](https://i-blog.csdnimg.cn/blog_migrate/a41deeb97fe3111a9a95e7e0c3a6f9a1.png)
优先队列
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;
}
高精度乘法
#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;
}