前言: 所有模板仅仅为笔者自己在算竞中用到的模板 由于笔者竞赛分都很低,高级模板还请前往查看old_yan的算竞模板
int gcd(int a, int b) {
while (b ^= a ^= b ^= a %= b);
return a;
}
long long lcm(long long a, long long b) {
return a * b / gcd(a, b);
}
//快速幂
template<typename T>
T power(T a, ll b) {
T res = 1;
for (; b; b /= 2, a *= a)
if (b % 2)
res *= a;
return res;
}
template<typename typC>
bool isPrime(typC num) {
if (num == 1 || num == 4)return 0;
if (num == 2 || num == 3)return 1;
if (num % 6 != 1 && num % 6 != 5)return 0;
typC tmp = sqrt(num);
for (int i = 5; i <= tmp; i += 6)
if (num % i == 0 || num % (i + 2) == 0)return 0;
return 1;
}
double get_angle(double x1, double y1, double x2, double y2, double x3, double y3) {
double theta = atan2(x1 - x3, y1 - y3) - atan2(x2 - x3, y2 - y3);
if (theta > M_PI)
theta -= 2 * M_PI;
if (theta < -M_PI)
theta += 2 * M_PI;
theta = abs(theta * 180.0 / M_PI);
return theta;
}
class UnionFind {
vector<int> root;
vector<int> rank;
public:
UnionFind(int size) {
root.resize(size);
rank.resize(size);
for (int i = 0; i < size; ++i) {
root[i] = rank[i] = i;
}
}
int find(int x) {
if (x == root[x]) return x;
return root[x] = find(root[x]);
}
void connect(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
if (rank[rootX] > rank[rootY]) {
root[rootY] = rootX;
} else if (rank[rootX] < rank[rootY]) {
root[rootX] = rootY;
} else {
root[rootY] = rootX;
rank[rootX] += 1;
}
}
}
bool isConnected(int x, int y) {
return find(x) == find(y);
}
};
namespace BellmanFord {
template <typename Tp>
struct BellmanFord {
struct Edge {
uint32_t from, to;
Tp distance;
};
std::vector<Edge> m_edges;
std::vector<Tp> m_distances;
std::vector<uint32_t> m_from;
uint32_t m_vertexNum;
Tp m_infiniteDistance;
BellmanFord(uint32_t _vertexNum, uint32_t _edgeNum, Tp _infiniteDistance = std::numeric_limits<Tp>::max() / 2) : m_distances(_vertexNum, _infiniteDistance), m_vertexNum(_vertexNum), m_infiniteDistance(_infiniteDistance) { m_edges.reserve(_edgeNum); }
void addEdge(uint32_t _a, uint32_t _b, Tp _distance) { m_edges.push_back({_a, _b, _distance}); }
void setDistance(uint32_t _i, Tp _distance = 0) { m_distances[_i] = _distance; }
template <bool GetPath = false>
bool calc() {
if constexpr (GetPath) m_from.resize(m_vertexNum, -1);
uint32_t lastUpdate = -1;
for (uint32_t i = 0; i < m_vertexNum && lastUpdate == i - 1; i++)
for (uint32_t index = 0; index < m_edges.size(); index++)
if (auto &[from, to, distance] = m_edges[index]; m_distances[from] != m_infiniteDistance && chmin(m_distances[to], m_distances[from] + distance)) {
lastUpdate = i;
if constexpr (GetPath) m_from[to] = index;
}
return lastUpdate != m_vertexNum - 1;
}
std::vector<uint32_t> getPath_edge(uint32_t _target) const {
std::vector<uint32_t> path;
for (uint32_t cur = _target; ~m_from[cur]; cur = m_edges[m_from[cur]].from) path.push_back(m_from[cur]);
std::reverse(path.begin(), path.end());
return path;
}
std::vector<uint32_t> getPath_vertex(uint32_t _target) const {
std::vector<uint32_t> path;
path.push_back(_target);
for (uint32_t cur = _target; ~m_from[cur];) path.push_back(cur = m_edges[m_from[cur]].from);
std::reverse(path.begin(), path.end());
return path;
}
};
}
namespace Dinic {
template<typename Tp>
struct Dinic {
struct RawEdge {
uint32_t from, to;
Tp cap;
};
struct Edge {
uint32_t to, rev;
Tp cap;
bool operator>(const Edge &other) const { return cap > other.cap; }
};
std::vector<RawEdge> m_rawEdges;
std::vector<Edge> m_edges;
std::vector<uint32_t> m_starts;
uint32_t m_vertexNum;
Dinic(uint32_t _vertexNum, uint32_t __edgeNum);
void addEdge(uint32_t __a, uint32_t __b, Tp __cap) { m_rawEdges.push_back({__a, __b, __cap}); }
void prepare() {
for (auto &[from, to, cap]: m_rawEdges)
if (from != to) {
m_starts[from + 1]++;
m_starts[to + 1]++;
}
std::partial_sum(m_starts.begin(), m_starts.end(), m_starts.begin());
m_edges.resize(m_starts.back());
uint32_t cursor[m_vertexNum];
std::copy(m_starts.begin(), m_starts.begin() + m_vertexNum, cursor);
for (auto &[from, to, cap]: m_rawEdges)
if (from != to) {
m_edges[cursor[from]] = Edge{to, cursor[to], cap};
m_edges[cursor[to]++] = Edge{from, cursor[from]++, 0};
}
}
template<typename _Compare = std::greater<Edge>>
void prepareSorted(_Compare __comp = _Compare()) {
prepare();
for (uint32_t i = 0; i < m_vertexNum; i++) {
uint32_t start = m_starts[i], end = m_starts[i + 1];
std::sort(m_edges.begin() + start, m_edges.begin() + end, __comp);
for (uint32_t j = start; j < end; j++) m_edges[m_edges[j].rev].rev = j;
}
}
Tp calc(uint32_t _source, uint32_t _target, Tp _infiniteCap = std::numeric_limits<Tp>::max() / 2) {
uint32_t queue[m_vertexNum], depth[m_vertexNum], it[m_vertexNum], end[m_vertexNum];
Tp res = 0;
for (uint32_t i = 0; i < m_vertexNum; i++) end[i] = m_starts[i + 1];
auto dfs = [&](auto self, uint32_t i, Tp _cap) {
if (i == _target || !_cap) return _cap;
Tp flow = 0, f;
for (uint32_t &cur = it[i]; cur != end[i]; cur++)
if (auto &[to, rev, cap] = m_edges[cur]; depth[i] + 1 == depth[to] &&
(f = self(self, to, std::min(_cap, cap))))
if (flow += f, _cap -= f, cap -= f, m_edges[rev].cap += f; !_cap) break;
return flow;
};
while (true) {
std::fill(depth, depth + m_vertexNum, -1);
uint32_t head = 0, tail = 0;
depth[_source] = 0;
queue[tail++] = _source;
while (head < tail)
for (uint32_t from = queue[head++], cur = m_starts[from], end = m_starts[from + 1];
cur < end; cur++)
if (auto &[to, rev, cap] = m_edges[cur]; cap && chmin(depth[to], depth[from] + 1))
queue[tail++] = to;
if (!~depth[_target]) break;
for (uint32_t i = 0; i < m_vertexNum; i++) it[i] = m_starts[i];
while (Tp flow = dfs(dfs, _source, _infiniteCap)) res += flow;
}
return res;
}
};
template<typename Tp>
Dinic<Tp>::Dinic(uint32_t _vertexNum, uint32_t _edgeNum) : m_starts(_vertexNum + 1, 0),
m_vertexNum(_vertexNum) {
m_rawEdges.reserve(_edgeNum);
}
}
class segmentTree {
int mod = 0x3f3f3f3f;
int a[100010];
struct Segment_Tree {
ll sum, add, mul;
int l, r;
} s[100010 * 4];
void update(int pos) {
s[pos].sum = (s[pos << 1].sum + s[pos << 1 | 1].sum) % mod;
return;
}
void pushdown(int pos) { //pushdown的维护
s[pos << 1].sum = (s[pos << 1].sum * s[pos].mul + s[pos].add * (s[pos << 1].r - s[pos << 1].l + 1)) % mod;
s[pos << 1 | 1].sum =
(s[pos << 1 | 1].sum * s[pos].mul + s[pos].add * (s[pos << 1 | 1].r - s[pos << 1 | 1].l + 1)) % mod;
s[pos << 1].mul = (s[pos << 1].mul * s[pos].mul) % mod;
s[pos << 1 | 1].mul = (s[pos << 1 | 1].mul * s[pos].mul) % mod;
s[pos << 1].add = (s[pos << 1].add * s[pos].mul + s[pos].add) % mod;
s[pos << 1 | 1].add = (s[pos << 1 | 1].add * s[pos].mul + s[pos].add) % mod;
s[pos].add = 0;
s[pos].mul = 1;
return;
}
void build_tree(int pos, int l, int r) { //建树
s[pos].l = l;
s[pos].r = r;
s[pos].mul = 1;
if (l == r) {
s[pos].sum = a[l] % mod;
return;
}
int mid = (l + r) >> 1;
build_tree(pos << 1, l, mid);
build_tree(pos << 1 | 1, mid + 1, r);
update(pos);
return;
}
void mul(int pos, int x, int y, int k) { //区间乘法
if (x <= s[pos].l && s[pos].r <= y) {
s[pos].add = (s[pos].add * k) % mod;
s[pos].mul = (s[pos].mul * k) % mod;
s[pos].sum = (s[pos].sum * k) % mod;
return;
}
pushdown(pos);
int mid = (s[pos].l + s[pos].r) >> 1;
if (x <= mid) mul(pos << 1, x, y, k);
if (y > mid) mul(pos << 1 | 1, x, y, k);
update(pos);
return;
}
void add(int pos, int x, int y, int k) { //区间加法
if (x <= s[pos].l && s[pos].r <= y) {
s[pos].add = (s[pos].add + k) % mod;
s[pos].sum = (s[pos].sum + k * (s[pos].r - s[pos].l + 1)) % mod;
return;
}
pushdown(pos);
int mid = (s[pos].l + s[pos].r) >> 1;
if (x <= mid) add(pos << 1, x, y, k);
if (y > mid) add(pos << 1 | 1, x, y, k);
update(pos);
return;
}
ll AskRange(int pos, int x, int y) { //区间询问
if (x <= s[pos].l && s[pos].r <= y) {
return s[pos].sum;
}
pushdown(pos);
ll val = 0;
int mid = (s[pos].l + s[pos].r) >> 1;
if (x <= mid) val = (val + AskRange(pos << 1, x, y)) % mod;
if (y > mid) val = (val + AskRange(pos << 1 | 1, x, y)) % mod;
return val;
}
};
class Trie {
public:
int nCnt;
vector<vector<int>> ch;
vector<int> f;
int newNode() {
ch.emplace_back(26, -1);
f.push_back(0);
return nCnt++;
}
void add(string &s) {
int now = 0;
for (char i: s) {
f[now]++;
int c = i - 'a';
if (ch[now][c] == -1) ch[now][c] = newNode();
now = ch[now][c];
}
f[now]++;
}
int query(string &s) {
int now = 0, ret = 0;
for (char i: s) {
if (now > 0) ret += f[now];
int c = i - 'a';
now = ch[now][c];
}
ret += f[now];
return ret;
}
};
template<typename T>
struct FenWick {
int N;
vector<T> arr;
FenWick(int sz): N(sz), arr(sz + 1, 0) {}
void update(int pos, T val) {
for (; pos <= N;pos |= (pos + 1)) {
arr[pos] += val;
}
}
// 获取 [1, pos] 的和
T get(int pos) {
T ret = 0;
for (; pos > 0; --pos) {
ret += arr[pos];
pos &= (pos + 1);
}
return ret;
}
// 获取 [l, r] 的和
T query(int l, int r) {
return get(r) - get(l - 1);
}
};
namespace Chtholly {
struct Node {
int l, r;
mutable int v;
Node(int il, int ir, int iv) : l(il), r(ir), v(iv) {}
bool operator<(const Node &arg) const {
return l < arg.l;
}
};
class Tree {
protected:
auto split(int pos) {
if (pos > _sz) return odt.end();
auto it = --odt.upper_bound(Node{pos, 0, 0});
if (it->l == pos) return it;
auto tmp = *it;
odt.erase(it);
odt.insert({tmp.l, pos - 1, tmp.v});
return odt.insert({pos, tmp.r, tmp.v}).first;
}
public:
Tree(int sz, int ini = 1) : _sz(sz), odt({Node{1, sz, ini}}) {}
virtual void assign(int l, int r, int v) {
auto itr = split(r + 1), itl = split(l);
// operations here
odt.erase(itl, itr);
odt.insert({l, r, v});
}
protected:
int _sz;
set<Node> odt;
};
}
template<typename iter, typename BinOp>
class SparseTable {
using T = typename remove_reference<decltype(*declval<iter>())>::type;
vector<vector<T>> arr;
BinOp binOp;
public:
SparseTable(iter begin, iter end, BinOp binOp) : arr(1), binOp(binOp) {
int n = distance(begin, end);
arr.assign(32 - __builtin_clz(n), vector<T>(n));
arr[0].assign(begin, end);
for (int i = 1; i < arr.size(); ++i) {
for (int j = 0; j < n - (1 << i) + 1; ++j) {
arr[i][j] = binOp(arr[i - 1][j], arr[i - 1][j + (1 << (i - 1))]);
}
}
}
T query(int lPos, int rPos) {
int h = floor(log2(rPos - lPos + 1));
return binOp(arr[h][lPos], arr[h][rPos - (1 << h) + 1]);
}
};
class SuffixArray {
private:
void radixSort(int n, int m, int w, vector<int> &sa, vector<int> &rk, vector<int> &bucket, vector<int> &idx) {
fill(all(bucket), 0);
for (int i = 0; i < n; ++i) idx[i] = sa[i];
for (int i = 0; i < n; ++i) ++bucket[rk[idx[i] + w]];
for (int i = 1; i < m; ++i) bucket[i] += bucket[i - 1];
for (int i = n - 1; i >= 0; --i) sa[--bucket[rk[idx[i] + w]]] = idx[i];
fill(all(bucket), 0);
for (int i = 0; i < n; ++i) idx[i] = sa[i];
for (int i = 0; i < n; ++i) ++bucket[rk[idx[i]]];
for (int i = 1; i < m; ++i) bucket[i] += bucket[i - 1];
for (int i = n - 1; i >= 0; --i) sa[--bucket[rk[idx[i]]]] = idx[i];
}
public:
SuffixArray(const string &s) :
n(s.length() + 1),
m(max((int) s.length() + 1, 300)),
rk(2, vector<int>((s.length() + 1) << 1)),
bucket(max((int) s.length() + 1, 300)),
idx(s.length() + 1),
sa(s.length() + 1),
ht(s.length()) {
for (int i = 0; i < n; ++i) ++bucket[rk[0][i] = s[i]];
for (int i = 1; i < m; ++i) bucket[i] += bucket[i - 1];
for (int i = n - 1; i >= 0; --i) sa[--bucket[rk[0][i]]] = i;
int pre = 1;
int cur = 0;
for (int w = 1; w < n; w <<= 1) {
swap(cur, pre);
radixSort(n, m, w, sa, rk[pre], bucket, idx);
for (int i = 1; i < n; ++i) {
if (rk[pre][sa[i]] == rk[pre][sa[i - 1]] and rk[pre][sa[i] + w] == rk[pre][sa[i - 1] + w]) {
rk[cur][sa[i]] = rk[cur][sa[i - 1]];
} else {
rk[cur][sa[i]] = rk[cur][sa[i - 1]] + 1;
}
}
}
for (int i = 0, k = 0; i < n - 1; ++i) {
if (k) --k;
while (s[i + k] == s[sa[rk[cur][i] - 1] + k]) ++k;
ht[rk[cur][i] - 1] = k;
}
}
vector<int> sa;
vector<int> ht;
private:
int n, m;
vector<vector<int>> rk;
vector<int> bucket, idx;
};
class KMP {
public:
/**
* @brief 统计目标串中有多少个模式串
* @param target 目标字符串
* @param pattern 模式字符串
* */
static int solve(string &target, string &pattern) {
int ans = 0;
int idxTarget = 0, idxPattern = 0;
vector<int> next(std::move(_prefix(pattern)));
while (idxTarget < target.length()) {
while (idxPattern != -1 and pattern[idxPattern] != target[idxTarget]) {
idxPattern = next[idxPattern];
}
++idxTarget;
++idxPattern;
if (idxPattern >= pattern.length()) {
++ans;
idxPattern = next[idxPattern];
}
}
return ans;
}
private:
static vector<int> _prefix(const string &pattern) {
int i = 0, j = -1;
vector<int> ret(pattern.length() + 1, -1);
while (i < pattern.length()) {
while (j != -1 and pattern[i] != pattern[j]) j = ret[j];
if (pattern[++i] == pattern[++j]) {
ret[i] = ret[j];
} else {
ret[i] = j;
}
}
return ret;
}
};
class StringHash {
public:
static unsigned BKDR(const std::string &str) {
unsigned seed = 131; // 31 131 1313 13131 131313 etc..
unsigned hash = 0;
for (auto c: str) {
hash = hash * seed + c;
}
return (hash & 0x7FFFFFFF);
}
static unsigned AP(const std::string &str) {
unsigned hash = 0;
for (int i = 0; i < str.length(); ++i) {
if (i & 1) {
hash ^= (~((hash << 11) ^ str[i] ^ (hash >> 5)));
} else {
hash ^= ((hash << 7) ^ str[i] ^ (hash >> 3));
}
}
return (hash & 0x7FFFFFFF);
}
static unsigned DJB(const std::string &str) {
unsigned hash = 5381;
for (auto c: str) {
hash += (hash << 5) + c;
}
return (hash & 0x7FFFFFFF);
}
static unsigned JS(const std::string &str) {
unsigned hash = 1315423911;
for (auto c: str) hash ^= ((hash << 5) + c + (hash >> 2));
return (hash & 0x7FFFFFFF);
}
static unsigned SDBM(const std::string &str) {
unsigned hash = 0;
for (auto c: str) hash = c + (hash << 6) + (hash << 16) - hash;
return (hash & 0x7FFFFFFF);
}
static unsigned PJW(const std::string &str) {
auto bits_in_unsigned_int = (unsigned) (sizeof(unsigned) * 8);
auto three_quarters = (unsigned) (bits_in_unsigned_int * 3 / 4);
auto one_eighth = (unsigned) (bits_in_unsigned_int / 8);
unsigned high_bits = (unsigned) (0xFFFFFFFF) << (bits_in_unsigned_int - one_eighth);
unsigned hash = 0;
unsigned test = 0;
for (auto c: str) {
hash = (hash << one_eighth) + c;
if ((test = hash & high_bits) != 0) {
hash = (hash ^ (test >> three_quarters)) & (~high_bits);
}
}
return (hash & 0x7FFFFFFF);
}
static unsigned ELF(const std::string &str) {
unsigned hash = 0, x = 0;
for (auto c: str) {
hash = (hash << 4) + c;
if ((x = hash & 0xF0000000ll) != 0) {
hash ^= (x >> 24);
hash &= (~x);
}
}
return (hash & 0x7FFFFFFF);
}
};
namespace Automaton {
struct ACNode {
vector<int> nex;
int fail;
int cnt;
ACNode() : nex(26, 0), cnt(0), fail(0) {}
};
class AC {
public:
AC() : nodes(1) {}
void insert(const string &arg) {
int cur = 0;
for (auto &c: arg) {
int to = c - 'a';
if (!nodes[cur].nex[to]) {
nodes[cur].nex[to] = (int) nodes.size();
nodes.emplace_back();
}
cur = nodes[cur].nex[to];
}
nodes[cur].cnt++;
}
void build() {
queue<int> Q;
for (int i = 0; i < 26; ++i) {
if (nodes[0].nex[i]) {
Q.push(nodes[0].nex[i]);
}
}
while (!Q.empty()) {
int cur = Q.front();
Q.pop();
for (int i = 0; i < 26; ++i) {
if (nodes[cur].nex[i]) {
nodes[nodes[cur].nex[i]].fail = nodes[nodes[cur].fail].nex[i];
Q.push(nodes[cur].nex[i]);
} else {
nodes[cur].nex[i] = nodes[nodes[cur].fail].nex[i];
}
}
}
}
int query(const string &arg) {
int cur = 0, ans = 0;
for (auto &c: arg) {
cur = nodes[cur].nex[c - 'a'];
for (int j = cur; j and nodes[j].cnt != -1; j = nodes[j].fail) {
ans += nodes[j].cnt;
nodes[j].cnt = -1;
}
}
return ans;
}
private:
vector<ACNode> nodes;
};
}
#define fastIO() ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
//摘自dianhsu大佬
int leastPowerOfTwo(int val){
return 32 - __builtin_clz(val - 1);
}
int greaterPowerOfTwo(int val){
return 32 - __builtin_clz(val);
}
template<typename typC, typename typD>
istream &operator>>(istream &cin, pair<typC, typD> &a) { return cin >> a.first >> a.second; }
template<typename typC>
istream &operator>>(istream &cin, vector<typC> &a) {
for (auto &x: a) cin >> x;
return cin;
}
template<typename typC, typename typD>
ostream &operator<<(ostream &cout, const pair<typC, typD> &a) { return cout << a.first << ' ' << a.second; }
template<typename typC, typename typD>
ostream &operator<<(ostream &cout, const vector<pair<typC, typD>> &a) {
for (auto &x: a) cout << x << '\n';
return cout;
}
template<typename typC>
ostream &operator<<(ostream &cout, const vector<typC> &a) {
int n = a.size();
if (!n) return cout;
cout << a[0];
for (int i = 1; i < n; i++) cout << ' ' << a[i];
return cout;
}
比赛链接:中国银联专场竞赛(2023届校园招聘专场) - 力扣(LeetCode)
排名:
给定一个链表,删除其中的所有偶数的节点
链表题大多数都可以用线性表水过去,笔者在此提供水版和正解版两种
正解:
using vi = vector<int>;
class Solution { //水题版
public:
ListNode* reContruct(ListNode* head) {
vi a;
while(head)a.push_back(head->val),head=head->next;
auto* ans=new ListNode(-1),*p=ans;
for(int v:a){
if(v%2)p->next=new ListNode(v),p=p->next;
}
return ans->next;
}
};
class Solution { //正解
public:
ListNode* reContruct(ListNode* head) {
auto *dummy=new ListNode(-1),*p=dummy;
while(head){
if(head->val%2){
p->next=new ListNode(head->val);
p=p->next;
}
head=head->next;
}
return dummy->next;
}
};
给定工程队的位置和所有补给点的位置,返回工程队选择补给的位置下标
二分查找每一个工程队的补给位置即可(这里采用map的lower_bound方法);
本题中使用了统计最大最小值的方法,来巧妙处理边界问题(实际上就是边界WA了好几次懒得改了)
#define pb push_back
class Solution {
public:
vector<int> explorationSupply(vector<int> &station, vector<int> &pos) {
vector<int> ans;
map<int, int> map;
int maxx=*max_element(all(station)), minn=*min_element(all(station));
for (int i = 0; i < station.size(); ++i) {
map[station[i]] = i;
}
for (int p: pos) {
if(p>=maxx){
ans.push_back(station.size()-1);
continue;
}else if (p<minn){
ans.push_back(0);
continue;
}
auto it = map.lower_bound(p);
if (it->first == p) {
ans.push_back(it->second);
continue;
}
if (it != map.begin())--it;
if (it == map.end()) {
--it;
ans.pb(it->second);
} else if (it == map.begin()) {
int i1 = it->second, i2 = i32;
++it;
if (it != map.end())i2 = it->second;
if (abs(station[i1] - p) <= abs(station[i2] - p))ans.pb(i1);
else ans.pb(i2);
} else {
int i1 = it->second, i2, i3 = i32;
--it;
i2 = it->second;
++it;
++it;
if (it != map.end())i3 = it->second;
int d1 = abs(p - station[i1]), d2 = abs(p - station[i2]), d3 = abs(p - station[i3]);
if (d1 <= d2 && d1 <= d3)ans.pb(i1);
else if (d2 <= d1 && d2 <= d3)ans.pb(i2);
else ans.pb(i3);
}
}
return ans;
}
};
给定容量为storeLimit的容器,在每一个使用区间内:
minSupply则从容器中去除电量(如果有富余)maxSupply则向容器中加入电量(如果还有空间)随时间变化,供电的最大最小需求也会不同,返回最终容器中剩余的电荷量
双指针模拟,一个指向供电,一个指向时间戳
class Solution {
public:
int StoredEnergy(int storeLimit, const vector<int>& power, const vector<vector<int>>& supply){
int i=0,j=0,m=power.size(),n=supply.size(),time=0;
int limit=0;
while (i<m){
if(time>=supply[min(j+1,n-1)][0])j=min(j+1,n-1);
int p=power[i];
if(p<=supply[j][1])limit=max(limit-(supply[j][1]-p),0);
else if(p>= supply[j][2])limit=min(storeLimit, limit+(p-supply[j][2]));
++i;
++time;
}
return limit;
}
};
后期退役ACM后再未更新
每次将给的区间内的字母+1或-1
区间和问题,可以使用差分数组来完成
class Solution {
public:
string shiftingLetters(string s, vector<vector<int>>& shifts) {
int n = s.size();
int cnt[n];
memset(cnt, 0, sizeof(cnt));
for(vector<int> a:shifts){
int start=a[0],end=a[1],op=a[2];
if (op==0){
cnt[start]--;
if (end+1<n)cnt[end+1]++;
} else{
cnt[start]++;
if (end+1<n)cnt[end+1]--;
}
}
for(int i = 1;i<n;++i)
cnt[i]+=cnt[i-1];
for (int i = 0; i < n; ++i) {
cnt[i]+=(s[i]-'a');
while(cnt[i]<0)cnt[i]+=26;
if(cnt[i]>=26)cnt[i]%=26;
}
string a;
for (int i = 0; i < n; ++i)
a+=(char)(cnt[i] + 'a');
return a;
}
};
每次删除数组中的一个数字,将数组分成多个子段
返回每次分割后的,子串的最大和
逆向遍历 + 并查集:
x和x+1,并把nums[x]加入到子段中class Solution {
typedef long long ll;
public:
vector<long long> maximumSegmentSum(vector<int>& nums, vector<int>& removeQueries) {
int n = nums.size();
int f[n+1];
iota(f, f + n + 1, 0);
ll sum[n+1];
memset(sum, 0, sizeof(sum));
function<int(int)> find = [&](int x) -> int { return f[x] == x ? x : f[x] = find(f[x]); };
std::vector<ll> ans(n);
for(int i=n-1; i>0; --i){
int x = removeQueries[i];
int to = find(x+1);
f[x]=to;
sum[to]+= sum[x] + nums[x];
ans[i-1] = max(ans[i], sum[to]);
}
return ans;
}
};
本题主要使用贪心算法解决,读者很快就可以想到,有趣的是在如何减少字符串相加带来的时间开销和处理前缀0的问题
flag来统计,由于0只能在结果字符串的中间部分,则若向字符串内增加了0,暂时将flag标记为1。若后续向0前方又增加了数字,此时便可以将flag置0(没有前缀0问题了)class Solution {
public:
string largestPalindromic(string num) {
map<int, int> map;
for(char c:num)map[c-'0']++;
if(map.size() == 1) {
return map.begin()->first == 0 ? "0" : num;
}
string a;
bool flag = false;
for(auto &it: map){
int to = it.second;
if(to > 1){
if(!flag && it.first == 0) flag = 1;
else flag = 0;
for(int i = 0; i < to / 2; ++i) {
a.push_back((char)(it.first + '0'));
}
if(to % 2 == 1) it.second = 1;
else it.second = 0;
}
}
string b;
if (flag) b = "";
else {
b = a;
reverse(b.begin(), b.end());
b += a;
}
int mid = b.size() / 2;
for (auto it = map.rbegin(); it != map.rend(); ++it){
if (it->second != 0) {
b.insert(b.begin() + mid, (char)(it->first + '0'));
break;
}
}
return b;
}
};
本题没有什么思维量,仅存的难点在于由TreeNode建图
作者此处使用了较为方便的unordered_map建图
class Solution {
public:
void dfs(unordered_map<int, vector<int>> &map, TreeNode* root){
if (root== nullptr) return;
if (root->left!= nullptr)map[root->val].push_back(root->left->val),map[root->left->val].push_back(root->val), dfs(map, root->left);
if (root->right!= nullptr)map[root->val].push_back(root->right->val),map[root->right->val].push_back(root->val), dfs(map,root->right);
}
int amountOfTime(TreeNode* root, int start) {
unordered_map<int, vector<int>> map;
dfs(map, root);
queue<int> queue;
queue.push(start);
int ans = 0, n = map.size();
unordered_set<int> set;
while (!queue.empty()){
int size = queue.size();
while (size-->0){
int pos = queue.front();
queue.pop();
if(set.count(pos)) continue;
set.insert(pos);
for(int v: map[pos]) if (!set.count(v)) queue.push(v);
}
++ans;
}
return ans - 1;
}
};
每次只能使用一辆垃圾车,每次必须收集完全部的垃圾
则可以对每一栋房子中的三种垃圾以及所有的三种垃圾计数
直到采集完成后结束
class Solution {
public:
int garbageCollection(vector<string>& garbage, vector<int>& travel) {
unordered_map<int, unordered_map<char, int>> map;
unordered_map<char, int> cnt;
int n = garbage.size();
for (int i=0; i<n; ++i) {
string g = garbage[i];
for(char c:g) map[i][c]++,++cnt[c];
}
char a[3] = {'P','M','G'};
int ans=0;
for (const char& c: a){
int p=0;
while (cnt[c]>0){
ans += map[p][c],cnt[c]-=map[p][c];
if (cnt[c]>0)ans+=travel[p],++p;
else break;
}
}
return ans;
}
};
给的一个行元素限制和列元素限制,返回按照限制之下所能构成的矩阵
由于元素之间只有先后出现之分,可以使用拓扑排序来确定每一个元素由下到上的出现次序和由右到左的出现次序
class Solution {
vector<int> topo_sort(int k, vector<vector<int>>& edges) {
vector<vector<int>> grid(k);
vector<int> in(k);
for(auto &e: edges){
int x = e[0]-1,y=e[1]-1;
grid[x].push_back(y);
in[y]++;
}
vector<int> order;
queue<int> queue;
for(int i=0; i<k; ++i)
if(in[i]==0)queue.push(i);
while(!queue.empty()){
int pos = queue.front();
queue.pop();
order.push_back(pos);
for(int v: grid[pos])if(--in[v]==0)queue.push(v);
}
return order;
}
public:
vector<vector<int>> buildMatrix(int k, vector<vector<int>>& rowConditions, vector<vector<int>>& colConditions) {
auto row=topo_sort(k, rowConditions), col=topo_sort(k,colConditions);
if(row.size()<k || col.size()<k)return {};
vector<int> pos(k);
for(int i=0; i<k; ++i)pos[col[i]]=i;
vector<vector<int>> ans(k,vector<int>(k,0));
for(int i=0; i<k; ++i)
ans[i][pos[row[i]]] = row[i] + 1;
return ans;
}
};
选择其中col列,检测是否每一行中所有的1都被选中了
数据量小,直接回溯即可
class Solution {
int ans = 0;
void check(vector<vector<>int> &mat, unordered_set<int> &set) {
int cnt = 0;
for (int i = 0; i < mat.size(); ++i) {
int j;
for (j = 0; j < mat[0].size(); ++j) {
if (mat[i][j] == 1) {
if (!set.count(j))break;
}
}
if (j == mat[0].size())++cnt;
}
ans = max(ans, cnt);
}
public:
void dfs(vector<vector<int>> &mat, int cols, unordered_set<int> set, int idx) {
if (idx == mat[0].size()&&cols!=0)return;
if (cols == 0) {
check(mat, set);
return;
}
for (int i = idx; i < mat[0].size(); ++i) {
set.insert(i);
dfs(mat, cols - 1, set, i + 1);
set.erase(i);
}
}
int maximumRows(vector<vector<int>> &mat, int cols) {
unordered_set<int> set;
dfs(mat, cols, set, 0);
return ans;
}
};
运行 k 个机器人 总开销 是 max(chargeTimes) + k * sum(runningCosts)
注意到需要取所有机器人中的最大充电时间,我们可以使用单调队列来动态维护区间最大值,双指针进行区间的选择
class Solution {
public:
int maximumRobots(vector<int>& chargeTimes, vector<int>& runningCosts, long long budget) {
int ans=0,left=0,right=0;
deque<int> deque;
long long sum=0ll;
while(right<chargeTimes.size()){
while(!deque.empty()&&chargeTimes[right]>=chargeTimes[deque.back()])
deque.pop_back();
deque.push_back(right);
sum += runningCosts[right];
while(!deque.empty()&&chargeTimes[deque.front()]+(right-left+1)*sum>budget){
if(deque.front()==left)deque.pop_front();
sum-=runningCosts[left],left++;
}
ans=max(ans,right-left+1),++right;
}
return ans;
}
};
一个数组中各个元素相与之和均为0,则这个数组为一个优雅子数组;返回长度最长的优雅子数组
模拟位运算+滑动窗口
根据位运算知识易知:
Java提供了使得数字转换为二进制数的方法,利用其进行模拟
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
class Solution {
boolean check(List<String> list, String s, int[] cnt) {
int n = s.length();
boolean f = true;
for (int i = n - 1, j = 0; i >= 0; --i, ++j) {
if (s.charAt(i) == '1' && cnt[j] == 1) {
f = false;
break;
}
}
return f;
}
public int longestNiceSubarray(int[] nums) {
int[] cnt = new int[32];
List<String> list = new ArrayList<>();
int maxLen = 0;
for (int n : nums) {
list.add(Integer.toBinaryString(n));
}
int ans = 1;
int l = 0, r = 0;
while (r < nums.length) {
String s = list.get(r);
if (check(list,s,cnt)){
ans= Math.max(ans, r-l+1);
++r;
for (int i = s.length() - 1, j = 0; i >= 0; --i, ++j) {
if (s.charAt(i)=='1')cnt[j]=1;
}
} else {
while (!check(list, s,cnt)){
String q=list.get(l);
for (int i = q.length() - 1, j = 0; i >= 0; --i, ++j) {
if (q.charAt(i)=='1')cnt[j]=0;
}
++l;
}
}
}
return ans;
}
}
给定一些区间段,按照一定的顺序将其分为n组,使得在n组中,每一个区间都不相交,返回n的最小数目
由于两区间段是否相交只取决于前一个区间的end和后一个区间的start
start <= end则两个区间会相交
例如[1,4]和[3,5]
则可以维护一个区间end值的优先队列,每次一添加新的区间时,只需要贪心地选择最小的那个end值进行判断
若start > end则可以加入该分组中,并更新该分组的末尾
若start <= end则不可加入任何分组(这一点接下来会有详细的分析),直接重新开一个分组,并将start对应的end加入其中
由于要求区间不可相交,则需要从start小的区间开始选取;因此需要对数组进行一个排序
#include "bits/stdc++.h"
#include "iomanip"
using namespace std;
#define all(a) a.begin(),a.end()
#define rall(a) rbegin(a), rend(a)
using vi = vector<int>;
class Solution {
public:
int minGroups(vector<vector<int>>& intervals) {
sort(all(intervals));//排序,满足从小到大选择区间
priority_queue<int, vi, greater<>> pq;
for (vi a:intervals){
int start=a[0],end=a[1];//当前请求加入分组的区间的起始值
if (pq.empty())pq.push(end);//空分组时
else{
if (start <= pq.top()){//不可加入已有的分组,直接以end新建分组
pq.push(end);
}else{//可加入已有分组,加入其中并更新区间尾数end
pq.pop();
pq.push(end);
}
}
}
//答案即为优先队列大小
return pq.size();
}
};
给定一个数组nums,对于其中的每一位i,找到包含这一位的一个子数组[i,len(nums)],使得该子数组中所有数或运算结果最大
枚举 + 二分
对于每一个数,结果最大则需要每一个二进制位上均为1,因此使用哈希表统计各个位上,存在1的下标,使用二分查找确定最远位置即可
string Binary(int x) {
string s = "";
while (x) {
if (x % 2 == 0) s = '0' + s;
else s = '1' + s;
x /= 2;
}
return s.size()==0 ? "0" : s;
}
using vi=vector<int>;
using vs=vector<string>;
class Solution {
public:
vector<int> smallestSubarrays(vector<int>& nums) {
int n=nums.size(),maxx=0;
vs s(n);
vi ans(n);
map<int, set<int>> map;
for (int i = 0; i < n; ++i) {
s[i]= Binary(nums[i]);
maxx=max(maxx,(int)s[i].size());
}
for (int i = 0; i < n; ++i) {
string b=s[i];
int j=b.size()-1;
while (j>=0){
if (b[j]=='1'){
map[b.size()-j].insert(i);
}
--j;
}
}
for (int i = 0; i < n; ++i) {
string target=s[i];
int last=i;
int j=maxx-1;
while (target.size() < maxx)target.insert(target.begin(), '0');
while (j>=0){
if (target[j]=='0'){
auto it=map[maxx-j].lower_bound(i);
if(it!=map[maxx-j].end())last=max(last, *it);
}
--j;
}
ans[i]=last-i+1;
}
return ans;
}
};
找到每一个字符的前缀在全部数组中的数量
| 字典树的每个节点记录以该节点为前缀的字符串有几个,计算答案的时候只需要将字符串所有前缀节点的值加起来即可。复杂度 $$\mathcal{O}(n | s | )$$,其中 | s | 是字符串长度 |
class Solution {
int nCnt;
vector<vector<int>> ch;
vector<int> f;
int newNode() {
ch.push_back(vector<int>(26, -1));
f.push_back(0);
return nCnt++;
}
void add(string &s) {
int now = 0;
for (int i = 0; i < s.size(); i++) {
f[now]++;
int c = s[i] - 'a';
if (ch[now][c] == -1) ch[now][c] = newNode();
now = ch[now][c];
}
f[now]++;
}
int query(string &s) {
int now = 0, ret = 0;
for (int i = 0; i < s.size(); i++) {
if (now > 0) ret += f[now];
int c = s[i] - 'a';
now = ch[now][c];
}
ret += f[now];
return ret;
}
public:
vector<int> sumPrefixScores(vector<string>& words) {
newNode();
for (string &s : words) add(s);
vector<int> ans;
for (string &s : words) ans.push_back(query(s));
return ans;
}
};
从(0,0)出发前往(m - 1, n - 1),返回路径上和可以整除k的路径数目
动态规划:
定义状态为\(f[i][j][l]\)表示以(i,j)为路径结尾时,整除k余数l的路径数目
状态转移:
由格子上方转移而来:
f[i][j][l] = (f[i][j][l] + f[i - 1][j][(l - grid[i][j] % k + k) % k]) % mod;
由格子左边转移而来
f[i][j][l] = (f[i][j][l] + f[i][j - 1][(l - grid[i][j] % k + k) % k]) % mod;
状态初始化:
f[0][0][grid[0][0] % k] = 1class Solution {
typedef long long ll;
const int mod = 1e9 + 7;
public:
int numberOfPaths(vector<vector<int>> &grid, int k) {
int m = grid.size(), n = grid[0].size();
ll f[m][n][k];
memset(f, 0, sizeof(f));
f[0][0][grid[0][0] % k] = 1;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
for (int l = 0; l < k; l++) {
if (i > 0)
f[i][j][l] = (f[i][j][l] + f[i - 1][j][(l - grid[i][j] % k + k) % k]) % mod;
if (j > 0)
f[i][j][l] = (f[i][j][l] + f[i][j - 1][(l - grid[i][j] % k + k) % k]) % mod;
}
}
}
return (int) (f[m - 1][n - 1][0] % mod);
}
};
在水平的位置上有一些目标需要击毁,你可以击毁的目标为 \(nums[i] + c * space\) 返回可以击毁的最大数目
“表达式中有space,那我去掉space会怎么样?”
两边同时整除space得到(MarkDown中|为整除)
\(nums[i] \mid space + 0\)
那么问题一下子就简洁了,直接计数即可出结果
class Solution {
public:
int destroyTargets(vector<int>& nums, int space) {
std::unordered_map<long long, int> map;
for(int v:nums) map[v % space]++;
int tot = 0, ans = INT_MAX;
for (int i: nums) {
int have = map[i % space];
if (tot == have) {
ans = min(ans, i);
} else if (tot < have) {
tot = have;
ans = i;
}
}
return ans;
}
};
给你两个正整数 n 和 target 。
如果某个整数每一位上的数字相加小于或等于 target ,则认为这个整数是一个 美丽整数 。
目标是找到最小的X则可以使用贪心法
由于是要让整个数字之和变小,则我们直接让每一个数字都变0,从最小开始即可
typedef long long ll;
class Solution {
bool check(ll n, int target) {
int to = 0;
while (n > 0){
to += n % 10;
n /= 10;
}
return to <= target;
}
public:
long long makeIntegerBeautiful(long long n, int target) {
if (check(n,target)) return 0;
ll t = n;
ll ans = 0, p = 1;
while (!check(n, target)) {
ll num = t % 10;
t /= 10;
if (num != 0){
n += ((10 - num) * p);
ans += p * (10 - num);
}
p *= 10;
t = n / p;
}
return ans;
}
};
缓存算法是指令的一个明细表,用于提示计算设备的缓存信息中哪些条目应该被删去。常见类型包括LFU、LRU、ARC、FIFO、MRU。
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
例如音乐播放软件的最近播放页面采取的就是LRU缓存来存储一定数量的音乐缓存。
class LRUCache {
private:
struct Node {
int key, value;
Node *pre = nullptr;
Node *next = nullptr;
Node(int key, int value) : key(key), value(value) {}
};
public:
int limit;
Node *head, *tail;
unordered_map<int, Node*> map;
LRUCache(int capacity) {
limit = capacity;
head = new Node(-1, -1), tail = new Node(-1, -1);
head->next = tail;
tail->pre = head;
}
void insertHead(Node *node) {
node->next = head->next;
node->pre = head;
head->next->pre = node;
head->next = node;
}
void deleteNode(Node *node) {
node->pre->next = node->next;
node->next->pre = node->pre;
}
void moveHead(Node *node, int val) {
deleteNode(node);
insertHead(node);
node->value = val;
}
int get(int key) {
auto it = map.find(key);
if (it == map.end()) return -1;
Node *node = it->second;
moveHead(node, node->value);
return node->value;
}
void put(int key, int value) {
auto it = map.find(key);
if (it != map.end()) {
Node *node = it->second;
moveHead(node, value);
} else {
if (map.size() == limit) {
Node *node = tail->pre;
deleteNode(node);
map.erase(node->key);
}
Node *newNode = new Node(key, value);
insertHead(newNode);
map.insert(make_pair(key, newNode));
}
}
};
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
在LFU中,我们需要根据使用频率和使用时间来对每一个节点的位置进行确定,C++中的set底层为红黑树,可以满足我们的要求
结构体定义为
struct Node {
int freq, time;
int key, value;
Node(int _freq, int _time, int _key, int _value) : freq(_freq), time(_time), key(_key), value(_value) {}
bool operator<(const Node &rhs) const {
return freq == rhs.freq ? time < rhs.time : freq < rhs.freq;
}
};
key, value为键值以及对应的数据
time, freq为对应键值的使用次数以及最后一次使用该键值的时间
下方还定义了一个比较方法:
变量:
time代表“当前时间”,每进行任意方法的调用,就对其进行+1更新,便于后续调用节点时更新使用时间set存储了当前缓存中的所有节点,并按照LFU进行排序limit为容量上限(这题居然有0空间的缓存)map快速定位到key对应的node具体的方法:
get方法返回对应key值的value,并更新节点位置;当未找到节点时直接返回-1
int get(int key) {
time++;
if (limit == 0) return -1;
auto it = map.find(key);
if (it == map.end()) return -1;
Node node = it->second;
set.erase(node);
node.freq++;
node.time = time;
set.insert(node);
it->second = node;
return node.value;
}
put方法用于新放置一个键:
key存在于缓存中,直接更新对应节点set的头节点(即最不经常使用的节点);上述判断完成后加入新节点即可void put(int key, int value) {
time++;
if (limit == 0) return;
auto it = map.find(key);
if (it == map.end()) {
if (map.size() == limit) {
auto node = set.begin();
map.erase(set.begin()->key);
set.erase(node);
}
Node node = Node(1, time, key, value);
set.insert(node);
map.insert(make_pair(key, node));
} else {
auto it = map.find(key);
Node node = it->second;
set.erase(node);
node.freq++;
node.time = time;
node.value = value;
set.insert(node);
it->second = node;
}
}
struct Node {
int freq, time;
int key, value;
Node(int _freq, int _time, int _key, int _value) : freq(_freq), time(_time), key(_key), value(_value) {}
bool operator<(const Node &rhs) const {
return freq == rhs.freq ? time < rhs.time : freq < rhs.freq;
}
};
class LFUCache {
private:
int limit;
int time = 0;
set<Node> set;
unordered_map<int, Node> map;
public:
LFUCache(int capacity) {
limit = capacity;
}
int get(int key) {
time++;
if (limit == 0) return -1;
auto it = map.find(key);
if (it == map.end()) return -1;
Node node = it->second;
set.erase(node);
node.freq++;
node.time = time;
set.insert(node);
it->second = node;
return node.value;
}
void put(int key, int value) {
time++;
if (limit == 0) return;
auto it = map.find(key);
if (it == map.end()) {
if (map.size() == limit) {
auto node = set.begin();
map.erase(set.begin()->key);
set.erase(node);
}
Node node = Node(1, time, key, value);
set.insert(node);
map.insert(make_pair(key, node));
} else {
auto it = map.find(key);
Node node = it->second;
set.erase(node);
node.freq++;
node.time = time;
node.value = value;
set.insert(node);
it->second = node;
}
}
};