1.需要一个数组或者字符串的目前的元素和前一个元素做相关运算时:
直接遍历这个序列,当 i 等于0时不满足条件,只有i 等于 1时才会执行if语句
for(int i = 0; i < len; ++i) {
if( i && 相关的运算) ...
}
2.与前一个元素做相关运算
我偏向定义一个变量pre代表前一个元素,然后从第二个位置开始遍历
感觉这样的代码确实有点多了,而且每次要更新pre的值
int pre = num[1];
for(int i=2;i<=n;i++)
{
相关的运算
pre = num[i];
}
3.存ASCII是否访问过,直接vis[200],开足够大的空间,ASCII本身也是一个数,直接用字符操作就行
vis[s[i]] ++;
4.判断一个序列是否是等差数列
遍历这个序列,将每个差分插入到一个集合中,如果最后集合的大小等于1,则为等差数列,还可以判断不同差分值的个数。
还可以通过set的方法访问首值和尾值
set<int>s;
int x = *s.begin();
int y = *s.rbegin();
5.在局部区域定义一个初始值为0的数组
int a[N]{};
可以发现最后a数组中的值都为0,和全局域定义的数组效果几乎一样。
vector数组第二维还可以赋值,将一个一维动态数组赋给一个二维数组的第二维
有一个定义数组的小技巧
//定义一个二维数组,第一维长度为200,第二维长度为0
vector<vector<int>> bk(200,vector<int>(0));
//m*n的二维vector,所有元素为0
vector<vector <int> > num(m ,vector<int>(n,0));
6.枚举二进制时的思路:一般数据范围比较小,需要枚举每个数,然后再对每个二进制位进行判断
for(int i=1;i<(1<<cnt);i++)//对每个数进行枚举
{
for(int j=0;j<cnt;j++)//对每个二进制位数进行判断
{
if(i&(1<<j))// i 代表这个数 1<<j是每个二进制位为1循环一遍,进行与运算,判断i的二进制位
{
}
}
7.定义vector用简单代码的输入,从0位置开始
vector<int> a(n);
for(auto &x : a){cin >> x;}
8.获得vector中的最大值(最小值)
max_element(v.begin(),v.end()) 返回的是最大值所在的迭代器,即位置
min_element(v.begin(),v.end()) 返回的是最大值所在的迭代器,即位置
vector<int >v;
int mx = *max_element(v.begin(),v.end());
int mn = *min_element(v.begin(),v.end());
9.c++结构体也可以写成员函数,通过打点来访问
struct node{
int cnt[N], ans = 0;
int add(int x)
{
......
}
}a[2];
10.卡空格写法
int f=1;
for(int i=1;i<=n;i++)
{
if(f)f=0; //如果为第一个元素,不输出空格,f置为0,接下来输出空格
else printf(" ");
}
11.求区间交集
有两个区间[a,b] [c,d],求区间交集长度
if(b<c||a>d) cout<<"无交集\n";
else ll len = min(b,d)-max(a,c);
12.结构体也可以进行强制转化,真的是没想到
struct node
{
int x,y,z;
}a[105];//如果要添加构造函数的话,创建结构体数组就会出错,因为没有传入参数,无法调用构造函数
a[1] = (node){1,2,3};//将1,2,3强行转换为结构体类型,顺便还能赋值
13.前缀和还可以这样来求
for(int i=1;i<=n;i++)
{
a[i] += a[i-1];
}//直接用一个数组替代了两个数组
14.结论一条:
偏序定理:序列中 递减子序列的个数等于序列的最长上升子序列的长度
- 用于求最长上升子序列和最长非上升子序列
15.输入的结束标志
当输入的数都是0时,便结束输入
while(cin>>x>>y>>z,x||y||z)
{
}
16.等于运算符(==)的优先级高于位运算符的优先级
乘除余 > 加减 > 左右移 > 关系运算符(>,<,>=,<=) > 与 异或 或 非 > 逻辑与或(&& || )> 赋值运算符 > (/=, %= ,*=)
17.找树的直径
- 任取一点作为起点,找到距该点最远的一个点u,(dfs,bfs)
- 再找距离u最远的一个点v,(dfs,bfs)
- 那么u和v之间的路径就是树的直径
18.输入输出同步解除和puts()不能同时使用
19.移位运算时一定要注意数据范围,要不然很容易吃这方面的亏
结果的数据类型取决于前面的数字
int x = 1ll<<i;
//一定要注意前面的数字1数据为long long类型
20.数组切段技巧,将相同元素的数组分段
for(int l=1,r=1;l<=n;l=r+1,r=l)
{
while(r < n && a[l]==a[r+1]) r++;
printf("%d %d",l,r);
}
21.lamda表达式排序操作
例对数组进行从大到小的排序
sort(a + 1, a + 1 + n, [](const int&A, const int&B) {
return A > B;
});
或者
sort(a+1,a+1+n,greater<int>());
22.格式控制技巧
//res数组是答案,进行输出
for(int i=1;i<=n;i++)
cout<<res[i]<<" \n"[i==n];
" \n"[i==n]
是格式控制,前面当作string字符串,后面是下标的表达式,当i!=n
时,下标取0,输出空格,当i==n
下标取1,输出换行。
23.Mex操作
数组a
的前缀取Mex操作:
vis[i]
:代表数组中数字i
是否出现,需要从前往后进行维护,不能一下预处理完
tmp
:代表从第一个元素开始到当前的元素,mex
值为tmp
int tmp = 0;
for(int i=1;i<=n;i++)
{
vis[a[i]] = true;//维护vis
while(vis[tmp]) tmp++;
}
24.浮点比较方法
const double eps = 1e-6;
bool cmp(double a,double b)
{
if(fabs(a-b)<eps) return true; //相等
return false;//不相等
}
25.取反操作:对正整数的每一位取反,除去符号位和前面的无效位。定义c(x)
为取反操作
例c(000101)=000010
,即只看后面的三位
设n
为2的幂次方,则
26.__builtin_popcount()
c++内置函数:统计一个数x
二进制中有多少个1
27.map
查找元素是否存在时,可以使用
1️⃣mp.find()
2️⃣ mp.count()
3️⃣ mp[key]
但是第三种情况,如果不存在对应的key
时,会自动创建一个键值对(产生一个额外的键值对空间)
所以为了不增加额外的空间负担,最好使用前两种方法
28.让n
变成小于等于n
且是m
的倍数(把n
削减成最大的m
的倍数)
n = n / m * m;
29.位运算枚举子集
一个数的初始值的二进制代表集合中的元素取和不取,1代表取,0代表不取。
代表初始集合,i
代表每次产生的新的子集
集合统一使用一个数进行表示
for(int i = s0; i ; i = (i - 1) & s0)//递减和原集合进行位运算
{
}
30.对一个数组排序的最小操作数 = n - 环数
详情看:https://blog.csdn.net/yunxiaoqinghe/article/details/113153795
31.有m
个区间,区间可能会有重合相交,每个区间都会对区间内的数字产生影响(可认为是区间加和),如果要单独剖出所有单个点不受影响的情况,可以进行如下想法:
区间存储可用以下数组:
vector<vector<int>> L(n + 1), R(n + 1);
//每次的读入
cin >> seg[i].first >> seg[i].second;
L[seg[i].first].push_back(seg[i].second);
R[seg[i].second].push_back(seg[i].first);
L[i]
代表以左端点i
为起始点的右端点数组
R[i]
代表以右端点i
为终端点的右端点数组
先将所有区间加和操作执行完,然后进行循环(类似扫描线),遇到区间开头就消除以此为左端点区间的影响,离开区间结尾就加上以此为右端点区间的影响。
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < int(L[i].size()); j++)
modify(1, 1, n, i, L[i][j], 1);
if(tr[1].mx - tr[1].mn > ans)
{
//区间不包括端点i的处理
pos = i;
ans = tr[1].mx - tr[1].mn;
}
for(int j = 0; j < int(R[i].size()); j++)
modify(1, 1, n, R[i][j], i, -1);
}
32.最长路采用的是double乘法计算最长路(即边权相乘),将边权log一下,转化为边权相加,防止爆精度。即边权求当做新的边权,然后跑最短路或者最长路时,比较的是dis + w
33.sqrt
函数可能会有精度误差,里面是double
的类型,如果传入long long
类型,强转为double
会有误差,建议sqrt
加强一下。
ll t = sqrt(x);
while(t * t > x) t--;
while((t + 1) * (t + 1) <= x) t++;
或者使用sqrtl
,参数类型是long double
ll t = sqrtl(x);
while(t * t > x) t--;
while((t + 1) * (t + 1) <= x) t++;
或者在里面直接转换为long double
类型。
ll t = sqrt((long double) x);
34.统计一个数的二进制下的位数
int cnt = 0;
ll x = 114514232;
while (x >> cnt) ++cnt;
- 使用auto定义lambda表达式递归函数
auto dfs = [&](auto&& dfs, int u, int p) -> void {
for (auto v: g[u]) {
if (v == p) continue;
dfs(dfs, v, u);
}
};