Compare commits

...

4 Commits

12 changed files with 425 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
/* There is an undirected tree with n nodes labeled from 0 to n - 1. You are given the integer n and a 2D integer array edges of length n - 1, where edges[i] = [ui, vi, wi] indicates that there is an edge between nodes ui and vi with weight wi in the tree.
You are also given a 2D integer array queries of length m, where queries[i] = [ai, bi]. For each query, find the minimum number of operations required to make the weight of every edge on the path from ai to bi equal. In one operation, you can choose any edge of the tree and change its weight to any value.
Note that:
Queries are independent of each other, meaning that the tree returns to its initial state on each new query.
The path from ai to bi is a sequence of distinct nodes starting with node ai and ending with node bi such that every two adjacent nodes in the sequence share an edge in the tree.
Return an array answer of length m where answer[i] is the answer to the ith query.
*/
#include <vector>
#include <array>
#include <bit>
#include <bitset>
#include <ranges>
using namespace std;
class Solution {
private:
vector<vector<int>> predecessor;
vector<int> depth;
vector<vector<array<int, 26>>> counter;
public:
vector<int> minOperationsQueries(int n, vector<vector<int>>& edges, vector<vector<int>>& queries) {
int m = bit_width((unsigned)n);
counter.resize(n, vector<array<int, 26>>(m)); // counter[k][i][j] -> 从结点k向上跳2^i权重j出现得次数权重取值范围1~26
predecessor.resize(n, vector<int>(m, -1));
depth.resize(n, 0);
vector<vector<pair<int, int>>> graph(n);
for (const auto& edge : edges) {
int u = edge[0], v = edge[1], w = edge[2] - 1;
graph[u].emplace_back(v, w);
graph[v].emplace_back(u, w);
}
auto dfs = [&](auto&& dfs, int x, int fa) -> void {
predecessor[x][0] = fa;
for (auto [y, w] : graph[x]) {
if (y != fa) {
counter[y][0][w] = 1;
depth[y] = depth[x] + 1;
dfs(dfs, y, x);
}
}
};
dfs(dfs, 0, -1);
for (int i = 1; i < m; i++) {
for (int x = 0; x < n; x++) {
if (int p = predecessor[x][i - 1]; p != -1) {
predecessor[x][i] = predecessor[p][i - 1];
for (int j = 0; j < 26; j++) {
counter[x][i][j] = counter[x][i - 1][j] + counter[p][i - 1][j];
}
}
}
}
vector<int> ans;
for (auto& query : queries) {
int u = query[0], v = query[1];
int path_len = depth[u] + depth[v];
array<int, 26> cur_counter = {0};
if (depth[u] > depth[v]) {
swap(u, v);
}
for (int k = depth[v] - depth[u]; k; k &= k - 1) {
int i = countr_zero(unsigned(k));
int p = predecessor[v][i];
for (int j = 0; j < 26; j++) {
cur_counter[j] += counter[v][i][j];
}
v = p;
}
if (u != v) {
for (int i = m - 1; i >= 0; i--) {
int pu = predecessor[u][i], pv = predecessor[v][i];
if (pu != pv) {
for (int j = 0; j < 26; j++) {
cur_counter[j] += counter[u][i][j] + counter[v][i][j];
}
u = pu, v = pv;
}
}
for (int j = 0; j < 26; j++) {
cur_counter[j] += counter[u][0][j] + counter[v][0][j];
}
u = predecessor[u][0];
}
path_len -= depth[u] * 2;
ans.push_back(path_len - ranges::max(cur_counter));
}
return ans;
}
};

View File

@@ -0,0 +1,73 @@
#include <vector>
#include <bit>
#include <bitset>
#include <cstdint>
class TreeAncestor {
private:
std::vector<std::vector<int>> predecessor;
std::vector<int> depth;
public:
TreeAncestor(std::vector<std::pair<int, int>>& edges) {
int n = edges.size() + 1;
int m = bit_width((unsigned)n);
predecessor.resize(n, std::vector<int>(m, -1));
depth.resize(n, 0);
std::vector<std::vector<int>> graph(n);
for (const auto& [u, v] : edges) {
graph[u].emplace_back(v);
graph[v].emplace_back(u);
}
auto dfs = [&](auto&& dfs, int x, int fa) -> void {
predecessor[x][0] = fa;
for (auto y : graph[x]) {
if (y != fa) {
depth[y] = depth[x] + 1;
dfs(dfs, y, x);
}
}
};
dfs(dfs, 0, -1);
for (int i = 1; i < m; i++) {
for (int x = 0; x < n; x++) {
if (int p = predecessor[x][i - 1]; p != -1) {
predecessor[x][i] = predecessor[p][i - 1];
}
}
}
}
int getKthAncestor(int node, int k) {
//for (int i = 0; i < bit_width((unsigned)k) && node != -1; i++) {
// if (k >> i & 1) {
// node = predecessor[node][i];
// }
//}
for (; k && node != -1; k &= k - 1) {
node = predecessor[node][countr_zero(unsigned(k))];
}
return node;
}
int getLowestCommonAncestor(int u, int v) {
if (depth[u] > depth[v]) {
std::swap(u, v);
}
for (int k = depth[v] - depth[u]; k; k &= k - 1) {
v = predecessor[v][countr_zero(unsigned(k))];
}
if (u != v) {
for (int i = predecessor[u].size() -1; i >= 0; i--) {
int pu = predecessor[u][i], pv = predecessor[v][i];
if (pu != pv) {
u = pu, v = pv;
}
}
}
return predecessor[u][0];
}
};

View File

@@ -0,0 +1,40 @@
/*
Search for the Kth Ancestor of a Tree Node
*/
#include <vector>
#include <bit>
#include <bitset>
#include <cstdint>
class TreeAncestor {
private:
std::vector<std::vector<int>> predecessor;
public:
TreeAncestor(int n, std::vector<int>& parent) {
int m = bit_width((unsigned)n);
predecessor.resize(n, std::vector<int>(m, -1));
for (int i = 0; i < n; i++) {
predecessor[i][0] = parent[i];
}
for (int i = 1; i < m; i++) {
for (int x = 0; x < n; x++) {
if (int p = predecessor[x][i - 1]; p != -1) {
predecessor[x][i] = predecessor[p][i - 1];
}
}
}
}
int getKthAncestor(int node, int k) {
// for (int i = 0; i < bit_width((unsigned)k) && node != -1; i++) {
// if (k >> i & 1) {
// node = predecessor[node][i];
// }
// }
for (; k && node != -1; k &= k - 1) {
node = predecessor[node][countr_zero(unsigned(k))];
}
return node;
}
};

View File

@@ -0,0 +1,35 @@
#include <unordered_map>
#include <queue>
#include <vector>
/*
The number container system can do the following:
1. insert or replace a number at the given index
2. return the smallest index for the given number in the system
*/
class NumberContainers {
private:
std::unordered_map<int, int> nums;
std::unordered_map<int, std::priority_queue<int, std::vector<int>, std::greater<int>>> heap;
public:
NumberContainers() = default;
void change(int index, int number) {
nums[index] = number;
heap[number].push(index);
}
int find(int number) {
while (!heap[number].empty() && nums[heap[number].top()] != number) {
heap[number].pop();
}
if (heap[number].empty()) {
return -1;
}
return heap[number].top();
}
};

View File

@@ -0,0 +1,48 @@
#include <unordered_map>
#include <queue>
/*
Task scheduling with changeable priorities
*/
class PriorityManager {
private:
std::unordered_map<int, int> info;
std::priority_queue<std::pair<int, int>> heap;
public:
PriorityManager(std::vector<std::pair<int, int>>& tasks) {
for (auto& task : tasks) {
int taskId = task.first, priority = task.second;
info[taskId] = priority;
heap.push(std::make_pair(priority, taskId));
}
}
void add(int taskId, int priority) {
info[taskId] = priority;
heap.push(std::make_pair(priority, taskId));
}
void edit(int taskId, int newPriority) {
info[taskId] = newPriority;
heap.push(std::make_pair(newPriority, taskId));
}
void rmv(int taskId) {
info.erase(taskId);
}
int execTop() {
while (!heap.empty()) {
auto task = heap.top();
heap.pop();
int priority = task.first, taskId = task.second;
if (info.find(taskId) != info.end() && info[taskId] == priority) {
rmv(taskId);
return taskId;
}
}
return -1;
}
};

View File

@@ -8,6 +8,12 @@
Return the number of fruit types that remain unplaced after all possible allocations are made.
*/
#include <vector>
#include <bit>
#include <bitset>
using namespace std;
class SegmentTree {
private:
vector<int> segVal;

View File

@@ -0,0 +1,8 @@
int greatest_common_divisor(int num1, int num2) {
while (num2 != 0) {
int temp = num2;
num2 = num1 % num2;
num1 = temp;
}
return num1;
}

46
trick/hash-structure.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include <vector>
#include <tuple>
#include <unordered_set>
template<typename T>
struct VectorHash {
static void has_combine(size_t& seed, const T& v) {
seed ^= hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
size_t operator() (const std::vector<T>& vec) const {
size_t seed = 0;
for (const T& v : vec) {
has_combine(seed, v);
}
return seed;
}
};
struct TupleHash {
template<typename T>
static void hash_combine(size_t& seed, const T& v) {
seed ^= hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
template<typename Tuple, size_t Index = 0>
static void hash_tuple(size_t& seed, const Tuple& t) {
if constexpr (Index < tuple_size_v<Tuple>) {
hash_combine(seed, get<Index>(t));
hash_tuple<Tuple, Index + 1>(seed, t);
}
}
template<typename... Ts>
size_t operator()(const std::tuple<Ts...>& t) const {
size_t seed = 0;
hash_tuple(seed, t);
return seed;
}
};
int main() {
std::unordered_set<std::vector<int>, VectorHash<int>> hash_set1;
std::unordered_set<std::tuple<int, int, int>, TupleHash> hash_set2;
return 0;
}

View File

@@ -0,0 +1,29 @@
#include <vector>
#include <string>
int knuth_morris_pratt(const std::string text, const std::string pattern) {
int n = text.length(), m = pattern.length();
std::vector<int> next(m, 0);
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && pattern[i] != pattern[j]) {
j = next[j - 1];
}
if (pattern[i] == pattern[j]) {
j++;
}
next[i] = j;
}
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && text[i] != pattern[j]) {
j = next[j - 1];
}
if (text[i] == pattern[j]) {
j++;
}
if (j == m) {
return i - m + 1;
}
}
return -1;
}

View File

@@ -0,0 +1,17 @@
#include <vector>
bool primality_check(int n) {
std::vector<bool> is_prime(n + 1, true);
if (n <= 2) {
return false;
}
is_prime[0] = is_prime[1] = false;
for (int i = 2; i * i <= n; i++) {
if (is_prime[i]) {
for (int j = i * i; j <= n; j += i) {
is_prime[j] = false;
}
}
}
return is_prime[n];
}

View File

@@ -0,0 +1,11 @@
long long quick_pow(int base, int power) {
long long ans = 1;
while (power > 0) {
if (power & 1) {
ans *= base;
}
base *= base;
power >>= 1;
}
return ans;
}

13
trick/split-text.cpp Normal file
View File

@@ -0,0 +1,13 @@
#include <vector>
#include <string>
std::vector<std::string> split(std::string target, std::string pattern) {
std::vector<std::string> res;
size_t start = 0, next = 0;
while ((next = target.find(pattern, start)) != std::string::npos) {
res.push_back(target.substr(start, next - start));
start = next + pattern.length();
}
res.push_back(target.substr(start));
return res;
}