小知识汇总 持续更新
🌮 vector的元素从大到小排序
sort(begin(v), end(v), greater<>())
🍣 需要按原顺序输出,同时需要sort的时候,开一个临时数组
vector<int> tmp = v;
🦊 关于区间求和问题 —— s[l, r] codeforces 1398C
- 公式:
s[r] - s[l - 1] = k
ans = 0
for r in 1 .. n
for l in 1 .. r
if S[l-1] == S[r] - K
ans += 1
output(ans)
- 问题:复杂度为O(N * N),复杂度太高了,那么有没有办法呢,是有的
- 分析:当 r 不断变大的时候,s[l - 1] 中有大部分是重复计算的,那么怎么避免重复计算呢,使用map存储前缀和出现的次数与数值,std::map
for(int r = 1; r <= n; r++){
m[s[r - 1]]++; // 将前缀和进行存储
ans += m[s[r] - k]; //根据公式,在存储的前缀和中寻找符合条件的个数
}
- 这样复杂度就降为O(NlogN)
🍍 并查集可以用来判环
if(find(a) == find(b)) 说明成环
🥶 记录前驱节点与遍历的方法
使用数组下标的方法进行遍历
for(int i = 2; i <= n; i++){
cin >> p[i];
}
for(int i = 1; i <= n; i++){
cin >> c[i];
c[p[i]] == c[i] ? 0 : ++s;
}
🧐 并查集的小技巧
并查集不仅能在不同的输入之间进行merge,还能讲自身的元素进行merge
cf 1263d
输入的字符串之间中有相同字母的归为一类
for(int i = 0; i < n; i++){
cin >> v[i];
int tmp = v[i][0] - 'a';
vis[tmp] = 1;
for(int j = 1; j < v[i].size(); j++){
merge(tmp, v[i][j] - 'a');
vis[v[i][j] - 'a'] = 1;
}
}
😷 关于xor运算的知识点
1、对于一群数以此进行xor操作,操作的顺序对结果没有影响。
2、1 xor 1== 0,0 xor 1 == 1, 0 xor 0 == 1,可以用来判断两个相同的数
💨 关于dsu的技巧(反向思维)
对于逐个删除的题目,可以反向去建图,相当于每次增加一个数
codeforces 722C
🙌 算法和复杂度的选择
1、在这种情况下,C++代码中的操作次数控制在 10e7∼10e8 为最佳。
2、在不同数据范围下,代码的时间复杂度和算法的选择方式 —— 传送门
🐡 关于map
- map是不用赋初值,初值就为0
- map的范围是不用自己定义的,范围为
map<int, int> a定义时的范围
🍺 数据范围
用到求和的时候直接开ll ans
🧉 前缀和 + 二分 cdoeforces 676C
用前缀和求出区间中需要改变字符的数量,每个右边界都对应一个最大的长度l
int cal(int st){
int l = 1, ret = 0;
for(int r = 1; r <= n; r ++){
while(l <= n && pre[r][st] - pre[l - 1][st] > k) l++;
ret = max(ret, r - l + 1);
}
return ret;
}
中间的循环可以转换成二分的,这题没卡数据
🥟 统计位的变化次数codeforces 1538F
十进制每个数字逢10,100…都会进位,因此只要求l , r l,rl,r有多少个进位数即可。
👻 前缀和的小技巧续 codeforces 1398C
s[r] - s[l - 1] = r - l + 1就可以同时减一将问题转化成s[r] - s[l - 1] = 0
🐷 优先队列的自定义排序
codeforces 1353D
自定义的是元素的优先级,这很重要。如果是结构体,那就pii换成node
struct cmp{
bool operator() (pii a, pii b ){//默认是less函数
//返回true时,a的优先级低于b的优先级(a排在b的后面)
if( a.y - a.x == b.y - b.x ) return a.x > b.x;
return a.y - a.x < b.y - b.x;
}
};
🦆 关于区间元素种类统计
codeforces 1234D
这里区间和就不适用用了,对于种类的统计,很显然会用到map或者set,map倾向于统计元素的个数,而set则判断是否存在,且是独一无二的
- 转换思路,统计各个字母出现的位置(唯一存在的),在查询是用lower_bound二分查找,判断set中是否存在区间中的pos
vector<set<int>> v-- 数据结构
v[s[pos] - 'a'].erase(pos);
v[c - 'a'].insert(pos);
s[pos] = c;
for(int i = 0; i < 26; i++){
auto it = v[i].lower_bound(l);
if(it != v[i].end() && *it <= r){
cnt++;
}
}
🛀 二维数组的初始化
vector<vector<int>> v(n, vector<int>(m)); //n行m列
👼 SLT中统计出现个数
count(a.begin(), a.end(), 查询内容);
不存在返回0,其他情况返回对应的个数
🦴 差分的写法
ans[l]++;
ans[r + 1]--;
for(int i = 2; i <= n; i++){
ans[i] += ans[i - 1];
}
留下一个伏笔 —— 二维差分
🐾 进制之间的转化
Acwing 2058
x —— 10
for(int i = 0; i < s.size(); i++){
sum *= x;
sum += (s[i] - '0');
}
🐤 双端队列bfs
Acwing 2019
Acwing 2060
算法适用的条件:
- 能将状态抽象成0/1,单纯的bfs只能将步长加一
- 适用最短路模型
算法特点
- 相当于dijkstra 对同一个点可以不断更新
- 遇到0 —— push_front() 遇到1 —— push_back();
void bfs(int x, int y){
deque<pii> q;
q.push_front({x, y});
memset(dis, 0x3f3f3f3f, sizeof dis);
dis[x][y] = 0;
while(!q.empty()){
auto now = q.front();
q.pop_front();
if(vis[now.x][now.y] == 1)
continue;
vis[now.x][now.y] = 1;
for(int i = 0; i < 4; i++){
int nowx = now.x + d[i][0];
int nowy = now.y + d[i][1];
if(nowx < 0 || nowx > N - 2 || nowy < 0 || nowy > N - 2)
continue;
if(dis[nowx][nowy] > dis[now.x][now.y] + map[nowx][nowy]){
dis[nowx][nowy] = dis[now.x][now.y] + map[nowx][nowy];
if(map[nowx][nowy] == 1)
q.push_back(make_pair(nowx, nowy));
else
q.push_front(make_pair(nowx, nowy));
}
}
}
🎍 反向思维
洛谷 p2011
适合反向思维的类型
- 图论 —— 反向建图(dsu)
- 先后关系对结果的影响很大
🌋 int - stirng 转换
string 转 int
char* s = "-111" => int intS = atoi(s)
string str = "234"=> int intStr = atoi(str.c_str());
int 转 string
string s = to_string(num)
🦉 判断数据范围合理暴力
当数据小于1e6/7的时候,想办法循环暴力
🐲 vector中的find函数的适用
- 存在返回迭代器
find(v.begin(), v.end(), 3) - 不存在 返回
v.end()
🌁 比较常见的边界条件情况
- 前导0
- 0
- 1
🛫 map的恶心点
map只要适用了就存在了
比如:if(m[i] != 0) 在之后的遍历中,i点的map也会遍历到,即使值为0
🚀 找规律问题
洛谷 p1028
无从下手的时候可以从小规模的数字进行枚举,找到递推的方程
💉 数论中gcd lcm详解
洛谷 p1029
题意:给定两个数x,y分别为gcd和lcm,求p,q的gcd和lcm为x,y的个数
素数分解式: 720 = 2^4 * 3^2 * 5^1
12 = 2^2 * 3^1 * 5^0
15 = 2^0 * 3^1 * 5^1
没有的非公共的指数为0
最大公约数:将两个数素数分解式中每个素因子的指数部分取两个中的最小值
—— (12, 15) = 2^0 * 3^1 * 5^0 = 3
最小公倍数:将两个数素数分解式中每个素因子的指数部分取两个中的最大值
—— [12, 15] = 2^2 * 3^1 * 5^1 = 60
🛠️ 选择问题
当存在选与不选的情况寻找最优情况,可以使用dfs进行判断(数据的选择)
🦝 substr字符串的截取
s.substr(起点,长度)
特别注意:第二个参数是长度而不是终点
🦆 具有共同gcd的数的特点
洛谷 p6068
假设答案是g
a = A * g
b = B * g
c = C * g
a + b + c = (A + B + C) * g
a + b + c固定,要使得g最大,(A + B + C)最小即可,由于a,b,c互不相等,那么A,B,C同样互不相等
- 综上所诉,寻找k,使得k >= 6且n & k == 0,答案就是n / k (k == A + B + C)
🦡 最大值的最小值
for循环先比较出但前点的最大点,对所有最大值求最小值
🏄♂️ lower_bound对于数组的返回值
int it = lower_bound(a, a + n, tmp) - a
返回当前值的下标
👏 从n个数中选择k个数
- 如果k个数确定,可以用嵌套for循环判断
- 如果k不确定,就用dfs或者动态规划

大本钟下送快递,上面摆,下面寄
浙公网安备 33010602011771号