线段树
已知一个数列,你需要进行下面两种操作:
将某区间每一个数加上 𝑘
k。
求出某区间每一个数的和。
#include <iostream>
#include <bits/stdc++.h>
using LL = long long;
LL n, a[100005], d[270000], b[270000];
void build(LL l, LL r, LL p) { // l:区间左端点 r:区间右端点 p:节点标号
if (l == r) {
d[p] = a[l]; // 将节点赋值
return;
}
LL m = l + ((r - l) >> 1);
build(l, m, p << 1), build(m + 1, r, (p << 1) | 1); // 分别建立子树
d[p] = d[p << 1] + d[(p << 1) | 1];
}
void update(LL l, LL r, LL c, LL s, LL t, LL p) {
if (l <= s && t <= r) {
d[p] += (t - s + 1) * c, b[p] += c; // 如果区间被包含了,直接得出答案
return;
}
LL m = s + ((t - s) >> 1);
if (b[p])
d[p << 1] += b[p] * (m - s + 1), d[(p << 1) | 1] += b[p] * (t - m),
b[p << 1] += b[p], b[(p << 1) | 1] += b[p];
b[p] = 0;
if (l <= m)
update(l, r, c, s, m, p << 1); // 本行和下面的一行用来更新p*2和p*2+1的节点
if (r > m) update(l, r, c, m + 1, t, (p << 1) | 1);
d[p] = d[p << 1] + d[(p << 1) | 1]; // 计算该节点区间和
}
LL getsum(LL l, LL r, LL s, LL t, LL p) {
if (l <= s && t <= r) return d[p];
LL m = s + ((t - s) >> 1);
if (b[p])
d[p << 1] += b[p] * (m - s + 1), d[(p << 1) | 1] += b[p] * (t - m),
b[p << 1] += b[p], b[(p << 1) | 1] += b[p];
b[p] = 0;
LL sum = 0;
if (l <= m)
sum =
getsum(l, r, s, m, p << 1); // 本行和下面的一行用来更新p*2和p*2+1的答案
if (r > m) sum += getsum(l, r, m + 1, t, (p << 1) | 1);
return sum;
}
int main() {
std::ios::sync_with_stdio(false);
LL q, i1, i2, i3, i4;
std::cin >> n >> q;
for (LL i = 1; i <= n; i++) std::cin >> a[i];
build(1, n, 1);
while (q--) {
std::cin >> i1 >> i2 >> i3;
if (i1 == 2)
std::cout << getsum(i2, i3, 1, n, 1) << std::endl; // 直接调用操作函数
else
std::cin >> i4, update(i2, i3, i4, 1, n, 1);
}
return 0;
}
已知一个数列,你需要进行下面三种操作:
将某区间每一个数乘上 x
将某区间每一个数加上 𝑥
求出某区间每一个数的和。
#include <iostream>
using ll = long long;
int n, m;
ll mod;
ll a[100005], sum[400005], mul[400005], laz[400005];
void up(int i) { sum[i] = (sum[(i << 1)] + sum[(i << 1) | 1]) % mod; }
void pd(int i, int s, int t) {
int l = (i << 1), r = (i << 1) | 1, mid = (s + t) >> 1;
if (mul[i] != 1) { // 懒标记传递,两个懒标记
mul[l] *= mul[i];
mul[l] %= mod;
mul[r] *= mul[i];
mul[r] %= mod;
laz[l] *= mul[i];
laz[l] %= mod;
laz[r] *= mul[i];
laz[r] %= mod;
sum[l] *= mul[i];
sum[l] %= mod;
sum[r] *= mul[i];
sum[r] %= mod;
mul[i] = 1;
}
if (laz[i]) { // 懒标记传递
sum[l] += laz[i] * (mid - s + 1);
sum[l] %= mod;
sum[r] += laz[i] * (t - mid);
sum[r] %= mod;
laz[l] += laz[i];
laz[l] %= mod;
laz[r] += laz[i];
laz[r] %= mod;
laz[i] = 0;
}
return;
}
void build(int s, int t, int i) {
mul[i] = 1;
if (s == t) {
sum[i] = a[s];
return;
}
int mid = s + ((t - s) >> 1);
build(s, mid, i << 1); // 建树
build(mid + 1, t, (i << 1) | 1);
up(i);
}
void chen(int l, int r, int s, int t, int i, ll z) {
int mid = s + ((t - s) >> 1);
if (l <= s && t <= r) {
mul[i] *= z;
mul[i] %= mod; // 这是取模的
laz[i] *= z;
laz[i] %= mod; // 这是取模的
sum[i] *= z;
sum[i] %= mod; // 这是取模的
return;
}
pd(i, s, t);
if (mid >= l) chen(l, r, s, mid, (i << 1), z);
if (mid + 1 <= r) chen(l, r, mid + 1, t, (i << 1) | 1, z);
up(i);
}
void add(int l, int r, int s, int t, int i, ll z) {
int mid = s + ((t - s) >> 1);
if (l <= s && t <= r) {
sum[i] += z * (t - s + 1);
sum[i] %= mod; // 这是取模的
laz[i] += z;
laz[i] %= mod; // 这是取模的
return;
}
pd(i, s, t);
if (mid >= l) add(l, r, s, mid, (i << 1), z);
if (mid + 1 <= r) add(l, r, mid + 1, t, (i << 1) | 1, z);
up(i);
}
ll getans(int l, int r, int s, int t,
int i) { // 得到答案,可以看下上面懒标记助于理解
int mid = s + ((t - s) >> 1);
ll tot = 0;
if (l <= s && t <= r) return sum[i];
pd(i, s, t);
if (mid >= l) tot += getans(l, r, s, mid, (i << 1));
tot %= mod;
if (mid + 1 <= r) tot += getans(l, r, mid + 1, t, (i << 1) | 1);
return tot % mod;
}
using std::cin;
using std::cout;
int main() { // 读入
cin.tie(nullptr)->sync_with_stdio(false);
int i, j, x, y, bh;
ll z;
cin >> n >> m >> mod;
for (i = 1; i <= n; i++) cin >> a[i];
build(1, n, 1); // 建树
for (i = 1; i <= m; i++) {
cin >> bh;
if (bh == 1) {
cin >> x >> y >> z;
chen(x, y, 1, n, 1, z);
} else if (bh == 2) {
cin >> x >> y >> z;
add(x, y, 1, n, 1, z);
} else if (bh == 3) {
cin >> x >> y;
cout << getans(x, y, 1, n, 1) << '\n';
}
}
return 0;
}
树状数组的区间加区间和模板
int t1[MAXN], t2[MAXN], n;
int lowbit(int x) { return x & (-x); }
void add(int k, int v) {
int v1 = k * v;
while (k <= n) {
t1[k] += v, t2[k] += v1;
// 注意不能写成 t2[k] += k * v,因为 k 的值已经不是原数组的下标了
k += lowbit(k);
}
}
int getsum(int *t, int k) {
int ret = 0;
while (k) {
ret += t[k];
k -= lowbit(k);
}
return ret;
}
void add1(int l, int r, int v) {
add(l, v), add(r + 1, -v); // 将区间加差分为两个前缀加
}
long long getsum1(int l, int r) {
return (r + 1ll) * getsum(t1, r) - 1ll * l * getsum(t1, l - 1) -
(getsum(t2, r) - getsum(t2, l - 1));
}
建树
// Θ(n) 建树
void init() {
for (int i = 1; i <= n; ++i) {
t[i] = sum[i] - sum[i - lowbit(i)];
}
}
最小回文分割
int f(string s){
vector<vector<int>> pre;
int n = s.size();
pre.assign(n, vector<int>(n, true));
for(int i=n-1;i>=0;--i){
for (int j =i+1;j<n;++j){
pre[i][j]=(s[i]==s[j])&&pre[i+1][j-1];
}
}
int ans=0;
for(int i =0;i<n;++i){
int j;
for(j=n-1;j>=i;--j){
if(pre[i][j]==1) {
ans++;
break;
}
}
i=j;
}
return ans-1;
}
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <climits>
using namespace std;
struct BigInt {
vector<int> d;
BigInt() {}
BigInt(string s) {
for (int i = s.size() - 1; i >= 0; --i)
d.push_back(s[i] - '0');
trim();
}
void trim() {
while (d.size() > 1 && d.back() == 0) d.pop_back();
}
string toString() const {
string s;
for (int i = d.size() - 1; i >= 0; --i) s.push_back(d[i] + '0');
return s;
}
};
BigInt add(const BigInt &a, const BigInt &b) {
BigInt c;
int carry = 0;
for (int i = 0; i < (int)max(a.d.size(), b.d.size()) || carry; ++i) {
int x = carry;
if (i < a.d.size()) x += a.d[i];
if (i < b.d.size()) x += b.d[i];
c.d.push_back(x % 10);
carry = x / 10;
}
c.trim();
return c;
}
bool lessThan(const BigInt &a, const BigInt &b) {
if (a.d.size() != b.d.size()) return a.d.size() < b.d.size();
for (int i = a.d.size() - 1; i >= 0; --i)
if (a.d[i] != b.d[i])
return a.d[i] < b.d[i];
return false;
}
BigInt f(int n, const string &s) {
int l = s.size();
vector<vector<BigInt>> dp(l, vector<BigInt>(n));
vector<vector<bool>> ok(l, vector<bool>(n, false));
for (int i = 0; i < l; ++i) {
dp[i][0] = BigInt(s.substr(0, i + 1));
ok[i][0] = true;
}
for (int i = 0; i < l; ++i) {
for (int j = 1; j < n; ++j) {
if (j > i) continue;
BigInt best; bool first = true;
for (int k = j - 1; k < i; ++k) {
if (!ok[k][j - 1]) continue;
BigInt right(s.substr(k + 1, i - k));
BigInt val = add(dp[k][j - 1], right);
if (first || lessThan(val, best)) {
best = val;
first = false;
}
}
if (!first) {
dp[i][j] = best;
ok[i][j] = true;
}
}
}
return dp[l - 1][n - 1];
}
int main() {
int n;
string s;
while (cin >> n >> s) {
cout << f(n+1, s).toString() << endl;
}
return 0;
}
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
struct Node {
int x, y, mask, dist;
};
int R, C, K;
char mp[205][205];
bool vis[205][205][1 << 5];
vector<pair<int,int>> portals;
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
int bitcount(int x) {
int cnt = 0;
while (x) {
cnt += (x & 1);
x >>= 1;
}
return cnt;
}
int bfs(int sx, int sy, int ex, int ey) {
memset(vis, 0, sizeof(vis));
deque<Node> q;
q.push_back({sx, sy, 0, 0});
vis[sx][sy][0] = true;
while(!q.empty()) {
Node cur = q.front(); q.pop_front();
if (cur.x == ex && cur.y == ey) {
if (bitcount(cur.mask) >= K)
return cur.dist;
}
for (int i = 0; i < 4; ++i) {
int nx = cur.x + dx[i];
int ny = cur.y + dy[i];
if (nx < 0 || ny < 0 || nx >= R || ny >= C) continue;
char c = mp[nx][ny];
if (c == '#') continue;
int new_mask = cur.mask;
if (c >= '0' && c <= '4')
new_mask |= (1 << (c - '0'));
if (!vis[nx][ny][new_mask]) {
vis[nx][ny][new_mask] = true;
q.push_back({nx, ny, new_mask, cur.dist + 1});
}
}
if (mp[cur.x][cur.y] == '$') {
for (auto [px, py] : portals) {
if (px == cur.x && py == cur.y) continue;
if (!vis[px][py][cur.mask]) {
vis[px][py][cur.mask] = true;
q.push_front({px, py, cur.mask, cur.dist});
}
}
}
}
return -1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while (T--) {
cin >> R >> C >> K;
portals.clear();
int sx, sy, ex, ey;
for (int i = 0; i < R; ++i) {
cin >> mp[i];
for (int j = 0; j < C; ++j) {
if (mp[i][j] == 'S') sx = i, sy = j;
else if (mp[i][j] == 'E') ex = i, ey = j;
else if (mp[i][j] == '$') portals.push_back({i,j});
}
}
int res = bfs(sx, sy, ex, ey);
if (res == -1) cout << "oop!\n";
else cout << res << "\n";
}
return 0;
}
树形dp
#include<iostream>
#include <vector>
#include<array>
#include <algorithm>
using namespace std;
using ll = long long;
int N;
vector<vector<int>> adj;
vector<array<ll,3>> dp;
void dfs(int u, int parent){
bool isLeaf = true;
ll sum_min012 = 0;
ll sum_dp1 = 0;
ll sum_min12 = 0;
vector<int> children;
for(int v: adj[u]){
if(v == parent) continue;
isLeaf = false;
dfs(v, u);
children.push_back(v);
sum_min012 += min({dp[v][0], dp[v][1], dp[v][2]});
sum_dp1 += dp[v][1];
sum_min12 += min(dp[v][1], dp[v][2]);
}
if(isLeaf){
dp[u][0] = 0;
dp[u][1] = 1e9;
dp[u][2] = 1;
return;
}
dp[u][2] = 1 + sum_min012;
dp[u][0] = sum_dp1;
ll best = 1e9;
for(int v: children){
ll cand = sum_min12 - min(dp[v][1], dp[v][2]) + dp[v][2];
best = min(best, cand);
}
dp[u][1] = best;
}
int main(){
cin >> N;
adj.assign(N+1, vector<int>());
dp.assign(N+1, {0,0,0});
for(int i=0;i<N-1;++i){
int u,v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs(1, 0);
cout << min(dp[1][1], dp[1][2]) << "\n";
return 0;
}
#include <iostream>
#include <numeric>
#include <vector>
#include <unordered_map>
using ll = long long;
using namespace std;
struct Edge
{
int u,v;// u->v
ll w;
};
ll minArborescenceSubset(const vector<int>& nodes, int root, const vector<Edge>& edges){
int n = nodes.size();
unordered_map<int,int> idx;//将node映射成int型数字
for(int i=0;i<n;++i) idx[nodes[i]]=i;
vector<Edge> es;//记录全部的合法边,及边的两端的节点都可从root到达
for(auto &e:edges){
if(idx.count(e.u)&&idx.count(e.v)){
es.push_back({idx[e.u],idx[e.v],e.w});
}
}
const ll INF = 1e18;
ll res = 0;//return
int r = idx[root];
vector<int> nodeIds(n);
iota(nodeIds.begin(),nodeIds.end(),0);//给nodeIds的元素依次赋值0,1,2,3···
//这里似乎没用到 ⬆️
while(true){
vector<ll> in(n,INF);//记录每个点的最小入边的权重
vector<int> from(n,-1);//记录每个点的最小入边的出发点
for(auto &e:es){
if(e.u!=e.v&&e.w<in[e.v]){
in[e.v]=e.w;
from[e.v] = e.u;
}
}
in[r]=0;//root的in设为0
int cntnode = 0;//如果存在环,cntnode将变为压缩环后的总节点数量
vector<int> visited(n,-1),id(n,-1);
for(int i=0;i<n;++i){
res += in[i];
int v = i;//从idx为i的节点一直向前搜索,判断是否成环
while(visited[v]!=i&&id[v]==-1&&v!=r){
visited[v] = i;//记录一下当前的节点是从哪个节点搜索出来的
v= from[v];//向前搜索
}
//通过id可以判断当前节点是否已经成为某个环的一部分
if(id[v]==-1&&v!=r){
//确保是因为成环才导致上面的循环结束的
for(int u = from[v];u!=v;u = from[u]) id[u]=cntnode;
id[v]=cntnode++;
}
}
if(cntnode==0) break;//如果没环产生,则当前即位最小树型图
//给未成环剩余的节点从新附上id
for(int i=0;i<n;++i){
if(id[i]==-1) id[i]=cntnode++;
}
for(auto &e:es){
e.w -= in[e.v];//这里是关键,在将环压缩成一个节点时如果存在环外的节点指向环内的节点,要将这个路径的权重减去被指向节点的最小入边的权重
e.u = id[e.u];//因为之前在计算res时已经将被指向节点的in值记录,如果之后为了全局的最优而选择在被指向节点处断开环就得在res中减去对应的in值,
e.v = id[e.v]; //因此直接将未来某个要减去的值提前在别的可能选中的边上减去
}
n =cntnode;
r = id[r];
}
return res;
}
int main(){
vector<int> nodes={1,2,3,4};
vector<Edge> e={{1,2,3},{1,3,2},{2,3,4},{1,4,1},{4,1,2},{2,3,1}};
int root = 1;
cout<<minArborescenceSubset(nodes,root,e);
return 0;
}
```cpp
/* AVL 树节点类 */
struct TreeNode {
int val{}; // 节点值
int height = 0; // 节点高度
TreeNode *left{}; // 左子节点
TreeNode *right{}; // 右子节点
TreeNode() = default;
explicit TreeNode(int x) : val(x){}
};
/* 获取节点高度 */
int height(TreeNode *node) {
// 空节点高度为 -1 ,叶节点高度为 0
return node == nullptr ? -1 : node->height;
}
/* 更新节点高度 */
void updateHeight(TreeNode *node) {
// 节点高度等于最高子树高度 + 1
node->height = max(height(node->left), height(node->right)) + 1;
}
/* 获取平衡因子 */
int balanceFactor(TreeNode *node) {
// 空节点平衡因子为 0
if (node == nullptr)
return 0;
// 节点平衡因子 = 左子树高度 - 右子树高度
return height(node->left) - height(node->right);
}
/* 右旋操作 */
TreeNode *rightRotate(TreeNode *node) {
TreeNode *child = node->left;
TreeNode *grandChild = child->right;
// 以 child 为原点,将 node 向右旋转
child->right = node;
node->left = grandChild;
// 更新节点高度
updateHeight(node);
updateHeight(child);
// 返回旋转后子树的根节点
return child;
}
/* 左旋操作 */
TreeNode *leftRotate(TreeNode *node) {
TreeNode *child = node->right;
TreeNode *grandChild = child->left;
// 以 child 为原点,将 node 向左旋转
child->left = node;
node->right = grandChild;
// 更新节点高度
updateHeight(node);
updateHeight(child);
// 返回旋转后子树的根节点
return child;
}
/* 执行旋转操作,使该子树重新恢复平衡 */
TreeNode *rotate(TreeNode *node) {
// 获取节点 node 的平衡因子
int _balanceFactor = balanceFactor(node);
// 左偏树
if (_balanceFactor > 1) {
if (balanceFactor(node->left) >= 0) {
// 右旋
return rightRotate(node);
} else {
// 先左旋后右旋
node->left = leftRotate(node->left);
return rightRotate(node);
}
}
// 右偏树
if (_balanceFactor < -1) {
if (balanceFactor(node->right) <= 0) {
// 左旋
return leftRotate(node);
} else {
// 先右旋后左旋
node->right = rightRotate(node->right);
return leftRotate(node);
}
}
// 平衡树,无须旋转,直接返回
return node;
}
/* 插入节点 */
void insert(int val) {
root = insertHelper(root, val);
}
/* 递归插入节点(辅助方法) */
TreeNode *insertHelper(TreeNode *node, int val) {
if (node == nullptr)
return new TreeNode(val);
/* 1. 查找插入位置并插入节点 */
if (val < node->val)
node->left = insertHelper(node->left, val);
else if (val > node->val)
node->right = insertHelper(node->right, val);
else
return node; // 重复节点不插入,直接返回
updateHeight(node); // 更新节点高度
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
node = rotate(node);
// 返回子树的根节点
return node;
}
/* 删除节点 */
void remove(int val) {
root = removeHelper(root, val);
}
/* 递归删除节点(辅助方法) */
TreeNode *removeHelper(TreeNode *node, int val) {
if (node == nullptr)
return nullptr;
/* 1. 查找节点并删除 */
if (val < node->val)
node->left = removeHelper(node->left, val);
else if (val > node->val)
node->right = removeHelper(node->right, val);
else {
if (node->left == nullptr || node->right == nullptr) {
TreeNode *child = node->left != nullptr ? node->left : node->right;
// 子节点数量 = 0 ,直接删除 node 并返回
if (child == nullptr) {
delete node;
return nullptr;
}
// 子节点数量 = 1 ,直接删除 node
else {
delete node;
node = child;
}
} else {
// 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点
TreeNode *temp = node->right;
while (temp->left != nullptr) {
temp = temp->left;
}
int tempVal = temp->val;
node->right = removeHelper(node->right, temp->val);
node->val = tempVal;
}
}
updateHeight(node); // 更新节点高度
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
node = rotate(node);
// 返回子树的根节点
return node;
}
```cpp
/* 加法哈希 */
int addHash(string key) {
long long hash = 0;
const int MODULUS = 1000000007;
for (unsigned char c : key) {
hash = (hash + (int)c) % MODULUS;
}
return (int)hash;
}
/* 乘法哈希 */
int mulHash(string key) {
long long hash = 0;
const int MODULUS = 1000000007;
for (unsigned char c : key) {
hash = (31 * hash + (int)c) % MODULUS;
}
return (int)hash;
}
/* 异或哈希 */
int xorHash(string key) {
int hash = 0;
const int MODULUS = 1000000007;
for (unsigned char c : key) {
hash ^= (int)c;
}
return hash & MODULUS;
}
/* 旋转哈希 */
int rotHash(string key) {
long long hash = 0;
const int MODULUS = 1000000007;
for (unsigned char c : key) {
hash = ((hash << 4) ^ (hash >> 28) ^ (int)c) % MODULUS;
}
return (int)hash;
}
```cpp
/* 回溯算法框架 */
void backtrack(State *state, vector<Choice *> &choices, vector<State *> &res) {
// 判断是否为解
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
// 不再继续搜索
return;
}
// 遍历所有选择
for (Choice choice : choices) {
// 剪枝:判断选择是否合法
if (isValid(state, choice)) {
// 尝试:做出选择,更新状态
makeChoice(state, choice);
backtrack(state, choices, res);
// 回退:撤销选择,恢复到之前的状态
undoChoice(state, choice);
}
}
}
/* 判断当前状态是否为解 */
bool isSolution(vector<TreeNode *> &state) {
return !state.empty() && state.back()->val == 7;
}
/* 记录解 */
void recordSolution(vector<TreeNode *> &state, vector<vector<TreeNode *>> &res) {
res.push_back(state);
}
/* 判断在当前状态下,该选择是否合法 */
bool isValid(vector<TreeNode *> &state, TreeNode *choice) {
return choice != nullptr && choice->val != 3;
}
/* 更新状态 */
void makeChoice(vector<TreeNode *> &state, TreeNode *choice) {
state.push_back(choice);
}
/* 恢复状态 */
void undoChoice(vector<TreeNode *> &state, TreeNode *choice) {
state.pop_back();
}
/* 回溯算法:例题三 */
void backtrack(vector<TreeNode *> &state, vector<TreeNode *> &choices, vector<vector<TreeNode *>> &res) {
// 检查是否为解
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
}
// 遍历所有选择
for (TreeNode *choice : choices) {
// 剪枝:检查选择是否合法
if (isValid(state, choice)) {
// 尝试:做出选择,更新状态
makeChoice(state, choice);
// 进行下一轮选择
vector<TreeNode *> nextChoices{choice->left, choice->right};
backtrack(state, nextChoices, res);
// 回退:撤销选择,恢复到之前的状态
undoChoice(state, choice);
}
}
}