小知识汇总 持续更新

🌮 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,其他情况返回对应的个数

🦴 差分的写法

Acwing 2041

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)

🦡 最大值的最小值

洛谷 p1927

for循环先比较出但前点的最大点,对所有最大值求最小值

🏄‍♂️ lower_bound对于数组的返回值

int it = lower_bound(a, a + n, tmp) - a
返回当前值的下标

👏 从n个数中选择k个数

  • 如果k个数确定,可以用嵌套for循环判断
  • 如果k不确定,就用dfs或者动态规划
posted @ 2022-01-19 19:30  Ansary  阅读(46)  评论(0)    收藏  举报