lower_bound e upper_bound são funções de busca binária incorporadas.

lower_bound - uma função que, em tempo logarítmico, encontra o menor elemento em uma matriz classificada que é maior ou igual ao valor dado k.
Toma os limites do array e o valor de k como argumentos.
Retorna um iterador para o elemento encontrado, ou para o final (não incluso) do array se tal elemento não existir.
Você pode ler mais na documentação.

upper_bound - uma função que em tempo logarítmico em uma matriz classificada encontra o menor elemento que é estritamente maior que o valor dado k.
Toma os limites do array e o valor de k como argumentos.
Retorna um iterador para o elemento encontrado, ou para o final (não incluso) do array se tal elemento não existir.
Você pode ler mais na documentação.

Vale esclarecer que o uso dessas funções em um conjunto ou multiconjunto não funciona em tempo logarítmico devido à falta de iteradores nas coleções de acesso aleatório acima.
No entanto, essas coleções têm métodos integrados correspondentes (ou seja, você precisa usá-los "através de um ponto").

Exemplos:
  vetor a = { 0, 1, 3, 5, 7 }; vector::iterator it; it = lower_bound(a.begin(), a.end(), 4); // *isso == 5 it = lower_bound(a.begin(), a.end(), 5); // *isso == 5 it = lower_bound(a.begin(), a.end(), 8); // é == a.end() it = upper_bound(a.begin(), a.end(), 4); // *isso == 5 it = upper_bound(a.begin(), a.end(), 5); // *isso == 7 it = upper_bound(a.begin(), a.end(), -1); // *isso == 0 // subtraindo iteradores, você pode obter o índice do elemento encontrado int ind = lower_bound(a.begin(), a.end(), 4) - a.begin(); // ind == 3 // precisa usar métodos em vez de funções para conjuntos e coleções semelhantes set s{ 1, 3, 5 }; set::iterator sit; sentar = s.lower_bound(3); // *senta == 3 senta = s.upper_bound(3); // *senta == 5  

unique - uma função que comprime todas as sequências de elementos consecutivos idênticos em um em tempo linear.
Como argumento, são passados ​​os limites do array, dentro dos quais é necessário aplicar compressão.
Um iterador é retornado ao novo final (não incluso) da matriz. Você deve ter cuidado com os elementos após o novo fim, mas antes do antigo, pois eles terão um valor indefinido.
Você pode ler mais na documentação.

Se você estiver usando esta função em um vetor, é conveniente redimensionar usando o resultado retornado (mais sobre isso abaixo).

Exemplos:
  vetor a = { 3, 3, 3, 2, 3, 3, 1, 1, 4, 5, 5 }; exclusivo(a.begin(), a.end()); // a = [3, 2, 3, 1, 4, 5, ?, ?, ?, ?, ?] // usando a função única é conveniente fazer // array auxiliar para compressão de coordenadas a = { 235, 10, 41, 10, 41, 41, 235, 500, 500 }; sort(a.begin(), a.end()); // a = [10, 10, 41, 41, 41, 235, 235, 500, 500] a.resize(unique(a.begin(), a.end()) - a.begin()); // a = [10, 41, 235, 500]  

merge - uma função que mescla dois arrays ordenados, ou seja, em tempo linear obtém um array ordenado, que consiste nos elementos do primeiro e do segundo array.
Leva 5 argumentos: dois limites para cada array e o limite esquerdo do destino (onde os elementos do array resultante serão colocados).
Mais detalhes podem ser encontrados na documentação.

Exemplos: // matrizes de origem devem ser classificadas vetor a = { 1, 3, 5, 7 }; vetor b = { 2, 4, 6 }; // precisa que o destino seja grande o suficiente vetor c(7); merge(a.begin(), a.end(), b.begin(), b.end(), c.begin()); // c = [1, 2, 3, 4, 5, 6, 7] // os elementos podem ser repetidos a = {1, 2, 4, 4}; b = { 2, 3, 3, 3, 4, 4 }; c.resize(10); merge(a.begin(), a.end(), b.begin(), b.end(), c.begin()); // c = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]  Esta função é muito útil no contexto de classificação por mesclagem.

nth_element é uma função que permite encontrar o n-ésimo elemento em uma matriz em ordem de classificação em tempo linear.
A função pega a extremidade esquerda da matriz, um iterador para a posição cujo valor na ordem de classificação deve ser encontrado e a extremidade direita da matriz.
Após a aplicação da função, o valor necessário estará localizado no local indicado pelo iterador, enquanto os valores restantes adquirirão uma ordem caótica, mas à esquerda do enésimo haverá valores não mais que isso, e para a direita não menos. Ou seja, deve-se entender que esta função destrói a ordem original dos elementos.
Você pode ler mais na documentação (https://www.cplusplus.com/reference/algorithm/nth_element/).

Exemplo: vetor a = { 4, 0, 3, 9, 2, 1, 8, 5, 6, 7 }; // procura elemento no índice 4 // presta atenção na ordem dos argumentos nth_element(a.begin(), a.begin() + 4, a.end()); // a = [#, #, #, #, 4, $, $, $, $, $] // onde # <= 4 e 4 <= $  

Uma permutação de comprimento n é uma coleção ordenada sem repetições dos números 1, 2, ..., n. Por exemplo, [3, 1, 2] e [5, 4, 3, 2, 1] são permutações, mas [1, 2, 1, 3] e [1, 2, 4] não são.

Se a tarefa for reduzida ao fato de que é necessário iterar sobre todas as permutações de comprimento n, então você pode usar um mecanismo conveniente em C ++, chamado "next_permutation".

Você pode ler mais sobre isso em documentação, mas o ponto é que esta função altera o array passado à permutação subseqüente na ordem lexicográfica (que geralmente é clara e seu nome).

Para usar next_permutation, você precisa incluir a biblioteca de algoritmos (ou seja, escreva #include <algorithm> no início do programa)

Exemplos: vetor arr; arr = { 1, 2, 3 }; // matriz é [1, 2, 3] next_permutation(arr.begin(), arr.end()); // passa todo o array para a função // array agora é [1, 3, 2] arr = { 2, 3, 1 }; // matriz é [2, 3, 1] next_permutation(arr.begin(), arr.end()); // passa todo o array para a função // array agora é [3, 1, 2] next_permutation(arr.begin() + 1, arr.begin() + 3); // é possível aplicar uma função a uma parte de um array, mas na prática isso raramente é necessário // array agora é [3, 2, 1]
Neste caso, a função tem um valor de retorno booleano que é true se a próxima permutação foi gerada e false se não houve próxima (caso em que a permutação máxima em ordem lexicográfica é passada para a função).
Isso torna possível usar a função em um loop, o que nos permitirá iterar todas as permutações de uma só vez. Devido à indexação 0, na prática muitas vezes é mais conveniente trabalhar com uma permutação de números de 0 a n - 1, embora uma permutação contenha formalmente números de 1 a n. Mas, felizmente, isso não leva a sobreposições adicionais no código, porque a função next_permutation é adaptada para permutações indexadas a 0 (e até elementos duplicados em uma matriz, mas você pode descobrir mais por conta própria).

Em geral, o código para iterar todas as permutações se parece com isto:   int; // tamanho da permutação vetorperm(n); // perm é a abreviação de "permutação", ou seja, "permutação" para (int i = 0; i < n; i++) perm[i] = i; // inicializa a permutação inicial 0, 1, ..., n - 1 fazer { // dentro do loop processamos a permutação atual } while (next_permutation(perm.begin(), perm.end())); // se não houver próxima permutação, então finalize o loop

Esse código é executado em O(n! * f(n)), onde f(n) é o tempo que você leva para processar uma permutação específica.