Dec. 13th 2025
Dec. 13th 2025
A. 动态开点
为了解决空间不够的情况
我们就可以到了该用这个节点的时候再申请空间
代码:
void update(int &id, int d, int posi, int left, int right) {
if (!id) id = ++cnt; // cnt if the number of nodes;
if (left == right) {
tree[id] = d; // +=
return ;
}
int mid = (left + right) >> 1;
if (posi <= mid) update(ls[id], d, posi, left, mid); // ls[] : left son, rs[] : right son
else update(rs[id], d, mid + 1, right);
maitain(id);
}
比较正常的 update :
void update(ll posi, ll d, ll id = 1, ll left = 1, ll right = n) {
if (left == right && left == posi) {
tree[id] += d;
return ;
}
ll mid = (left + right) >> 1;
if (posi <= mid) update(posi, d, ls(id), left, mid);
else update(posi, d, rs(id), mid + 1, right);
maintain(id);
}
就多了一行:
if (!id) id = ++cnt
然后将 ls()、rs() 改成数组
!\(\textcolor{red}{\text{注意}}\)!:因为在 update 中 id 传的是地址,所以不能访问一个常量
例题
【模板】普通平衡树
P3369 【模板】普通平衡树
题目描述
您需要动态地维护一个可重集合 \(M\),并且提供以下操作:
- 向 \(M\) 中插入一个数 \(x\)。
- 从 \(M\) 中删除一个数 \(x\)(若有多个相同的数,应只删除一个)。
- 查询 \(M\) 中有多少个数比 \(x\) 小,并且将得到的答案加一。
- 查询如果将 \(M\) 从小到大排列后,排名位于第 \(x\) 位的数。
- 查询 \(M\) 中 \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)。
- 查询 \(M\) 中 \(x\) 的后继(后继定义为大于 \(x\),且最小的数)。
对于操作 \(3,5,6\),不保证当前可重集中存在数 \(x\)。
对于操作 \(5,6\),保证答案一定存在。
输入格式
第一行为 \(n\),表示操作的个数,下面 \(n\) 行每行有两个数 \(\text{opt}\) 和 \(x\),\(\text{opt}\) 表示操作的序号($ 1 \leq \text{opt} \leq 6 $)。
输出格式
对于操作 \(3,4,5,6\) 每行输出一个数,表示对应答案。
输入输出样例 #1
输入 #1
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
输出 #1
106465
84185
492737
说明/提示
【数据范围】
对于 \(100\%\) 的数据,\(1\le n \le 10^5\),\(|x| \le 10^7\)。
来源:Tyvj1728,原名:普通平衡树。
在此鸣谢!
View Code
#include<bits/stdc++.h>
using namespace std;
using lf = double;
using ll = long long;
using ull = unsigned long long;
const int maxn = 1e5 + 5;
const ll MIN_X = -1e7;
const ll MAX_X = 1e7;
int n;
ll cnt = 1, root = 1;
ll a[maxn];
ll tree[maxn * 40];
ll ls[maxn * 40], rs[maxn * 40];
void maintain(ll id) {
tree[id] = tree[ls[id]] + tree[rs[id]];
}
void update(ll posi, ll d, ll &id, ll left, ll right) {
if (!id) id = ++cnt;
if (left == right && left == posi) {
tree[id] += d;
return ;
}
ll mid = (left + right) >> 1;
if (posi <= mid) update(posi, d, ls[id], left, mid);
else update(posi, d, rs[id], mid + 1, right);
maintain(id);
}
ll query(ll L, ll R, ll id, ll left, ll right) {
if (!id) return 0;
if (L <= left && right <= R) return tree[id];
ll mid = (left + right) >> 1;
ll res = 0;
if (L <= mid) res += query(L, R, ls[id], left, mid);
if (R > mid) res += query(L, R, rs[id], mid + 1, right);
return res;
}
ll kth_element(ll k, ll id, ll left, ll right) {
if (left == right) return left;
ll mid = (left + right) >> 1;
if (k <= tree[ls[id]]) return kth_element(k, ls[id], left, mid);
else return kth_element(k - tree[ls[id]], rs[id], mid + 1, right);
}
ll frank(ll x) {
return query(MIN_X, x - 1, 1, MIN_X, MAX_X) + 1;
}
ll prev(ll x) {
return kth_element(frank(x) - 1, 1, MIN_X, MAX_X);
}
ll suf(ll x) {
return kth_element(frank(x + 1), 1, MIN_X, MAX_X);
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
int opt, x;
cin >> opt >> x;
if (opt == 1) update(x, 1, root, MIN_X, MAX_X);
else if (opt == 2) update(x, -1, root, MIN_X, MAX_X);
else if (opt == 3) cout << frank(x) << "\n";
else if (opt == 4) cout << kth_element(x, 1, MIN_X, MAX_X) << "\n";
else if (opt == 5) cout << prev(x) << "\n";
else cout << suf(x) << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// freopen("T1.in", "r", stdin);
// freopen("T1.out", "w", stdout);
int T = 1;
// cin >> T;
while (T--) solve();
return 0;
}
CF915E Physical Education Lessons
View Code
#include<bits/stdc++.h>
using namespace std;
using lf = double;
using ll = long long;
using ull = unsigned long long;
const int maxn = 3e5 + 5;
const int MIN_X = 0;
const int MAX_X = 1e9;
int n, q;
int cnt = 1, root = 1;
int a[maxn];
int tree[maxn * 70];
int lazy_tag[maxn * 70];
int ls[maxn * 70], rs[maxn * 70];
void maintain(int id) {
tree[id] = tree[ls[id]] + tree[rs[id]];
}
void addtag(int &id, int d, int left, int right) {
if (!id) id = ++cnt;
tree[id] = d * (right - left + 1);
lazy_tag[id] = d;
}
void pushdown(int id, int left, int right) {
if (lazy_tag[id]) {
int mid = left + ((right - left) >> 1);
addtag(ls[id], lazy_tag[id], left, mid);
addtag(rs[id], lazy_tag[id], mid + 1, right);
lazy_tag[id] = 0;
}
}
void update(int L, int R, int d, int &id, int left, int right) {
if (!id) id = ++cnt;
if (L <= left && right <= R) {
// tree[id] += d;
addtag(id, d, left, right);
return ;
}
pushdown(id, left, right);
int mid = left + ((right - left) >> 1);
if (L <= mid) update(L, R, d, ls[id], left, mid);
if (R > mid) update(L, R, d, rs[id], mid + 1, right);
maintain(id);
}
int query(int L, int R, int id, int left, int right) {
if (!id) return 0;
if (L <= left && right <= R) return tree[id];
pushdown(id, left, right);
int mid = left + ((right - left) >> 1);
int res = 0;
if (L <= mid) res += query(L, R, ls[id], left, mid);
if (R > mid) res += query(L, R, rs[id], mid + 1, right);
return res;
}
int kth_element(int k, int id, int left, int right) {
if (left == right) return left;
int mid = left + (right - left) >> 1;
if (k <= tree[ls[id]]) return kth_element(k, ls[id], left, mid);
else return kth_element(k - tree[ls[id]], rs[id], mid + 1, right);
}
int frank(int x) {
return query(MIN_X, x - 1, 1, MIN_X, MAX_X) + 1;
}
int prev(int x) {
return kth_element(frank(x) - 1, 1, MIN_X, MAX_X);
}
int suf(int x) {
return kth_element(frank(x + 1), 1, MIN_X, MAX_X);
}
void solve() {
cin >> n >> q;
update(1, n, 1, root, MIN_X, MAX_X);
for (int i = 1; i <= q; i++) {
int l, r, opt;
cin >> l >> r >> opt;
update(l, r, opt - 1, root, MIN_X, MAX_X);
cout << query(1, n, 1, MIN_X, MAX_X) << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// freopen("T1.in", "r", stdin);
// freopen("T1.out", "w", stdout);
int T = 1;
// cin >> T;
while (T--) solve();
return 0;
}
【模板】线段树 1.5
View Code
#include<bits/stdc++.h>
using namespace std;
using lf = double;
using ll = unsigned long long;
//using ull = long long;
const int maxn = 1e5 + 5;
const ll MIN_X = 1;
const ll MAX_X = 1e9;
int n, m;
ll cnt = 1, root = 1;
ll a[maxn];
ll tree[maxn * 70];
ll lazy_tag[maxn * 70];
ll ls[maxn * 70], rs[maxn * 70];
void maintain(ll id) {
tree[id] = tree[ls[id]] + tree[rs[id]];
}
void addtag(ll &id, ll d, ll left, ll right) {
if (!id)
id = ++cnt;
// tree[id] = (left + right) * (right - left + 1) >> 1;
tree[id] += (right - left + 1) * d;
lazy_tag[id] += d;
}
void pushdown(ll id, ll left, ll right) {
if (lazy_tag[id]) {
ll mid = (left + right) >> 1;
addtag(ls[id], lazy_tag[id], left, mid);
addtag(rs[id], lazy_tag[id], mid + 1, right);
lazy_tag[id] = 0;
}
}
void update(ll L, ll R, ll d, ll &id, ll left, ll right) {
if (!id)
id = ++cnt;
// tree[id] = (left + right) * (right - left + 1) >> 1;
if (L <= left && right <= R) {
addtag(id, d, left, right);
return ;
}
pushdown(id, left, right);
ll mid = (left + right) >> 1;
if (L <= mid) update(L, R, d, ls[id], left, mid);
if (R > mid) update(L, R, d, rs[id], mid + 1, right);
maintain(id);
}
ll query(ll L, ll R, ll &id, ll left, ll right) {
if (!id)
id = ++cnt;
// tree[id] = (left + right) * (right - left + 1) >> 1;
if (L <= left && right <= R) return tree[id];
pushdown(id, left, right);
ll mid = (left + right) >> 1;
ll res = 0;
if (L <= mid) res += query(L, R, ls[id], left, mid);
if (R > mid) res += query(L, R, rs[id], mid + 1, right);
return res;
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int opt;
cin >> opt;
if (opt == 1) {
ll l, r, k;
cin >> l >> r >> k;
update(l, r, k, root, 1, n);
} else {
ll l, r;
cin >> l >> r;
cout << ((l + r) * (r - l + 1) >> 1) + query(l, r, root, 1, n) << "\n";
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// freopen("T1.in", "r", stdin);
// freopen("T1.out", "w", stdout);
int T = 1;
// cin >> T;
while (T--) solve();
return 0;
}
B.周测
T1 hotel
组内链接
题目描述
奶牛们正北上加拿大雷湾进行文化之旅,准备在苏必利尔湖阳光明媚的湖畔享受假期。作为称职的旅行代理,贝茜将著名的坎伯兰街公牛麋鹿酒店定为它们的下榻之处。这座巨型酒店拥有N间客房(1 ≤ N ≤ 50,000),所有房间都位于一条极长走廊的同一侧(当然是为了更好地欣赏湖景)。
奶牛和其他旅客以Di(1 ≤ Di ≤ N)为团体规模抵达前台办理入住。每个团体i会向当值的麋鹿管理员坎穆申请Di间连续客房。若有可用连续房间,坎穆会分配房号r至r+Di-1的序列;若无可用连续房间,则会礼貌推荐其他住宿。坎穆总是尽可能选择最小的r值。
旅客也会以连续房间为单位退房。每次退房请求包含参数Xi和Di,表示腾空Xi至Xi+Di-1号房间(1 ≤ Xi ≤ N-Di+1)。这些房间在退房前可能部分或全部本就空置。
你的任务是协助坎穆处理M次(1 ≤ M < 50,000)入住/退房请求。酒店初始处于全空状态。
输入格式
- 第1行:两个空格分隔的整数:N和M
- 第2..M+1行:第i+1行包含以下两种格式之一的请求:
(a) 两个空格分隔的整数表示入住请求:1和Di
(b) 三个空格分隔的整数表示退房请求:2、Xi和Di
输出格式
- 第1...行:对每个入住请求,输出一行整数r表示分配到的连续房间起始房号。若无法满足请求,则输出0。
原题
View Code
#include<bits/stdc++.h>
using namespace std;
using lf = double;
using ll = long long;
using ull = unsigned long long;
const int maxn = 5e4 + 5;
struct node{
ll l, r;
ll sum;
};
int n, m;
node tree[maxn << 2];
ll lazy_tag[maxn << 2];
ll ls(ll id) {return id << 1;}
ll rs(ll id) {return id << 1 | 1;}
void maitain(ll id, ll left, ll right) {
ll mid = (left + right) >> 1;
tree[id].l = tree[ls(id)].l;
if (tree[ls(id)].l == mid - left + 1)
tree[id].l += tree[rs(id)].l;
tree[id].r = tree[rs(id)].r;
if (tree[rs(id)].r == right - mid)
tree[id].r += tree[ls(id)].r;
tree[id].sum = max({tree[ls(id)].sum, tree[rs(id)].sum, tree[ls(id)].r + tree[rs(id)].l});
}
void pushdown(ll id, ll left, ll right) {
if (lazy_tag[id] != -1) {
lazy_tag[ls(id)] = lazy_tag[rs(id)] = lazy_tag[id];
if (lazy_tag[id] == 1) {
tree[ls(id)].l = tree[ls(id)].r = tree[ls(id)].sum = 0;
tree[rs(id)].l = tree[rs(id)].r = tree[rs(id)].sum = 0;
} else {
ll mid = (left + right) >> 1;
tree[ls(id)].l = tree[ls(id)].r = tree[ls(id)].sum = mid - left + 1;
tree[rs(id)].l = tree[rs(id)].r = tree[rs(id)].sum = right - mid;
}
lazy_tag[id] = -1;
}
}
void build(ll id, ll left, ll right) {
lazy_tag[id] = -1;
if (left == right) return tree[id].l = tree[id].r = tree[id].sum = 1, void();
ll mid = (left + right) >> 1;
build(ls(id), left, mid);
build(rs(id), mid + 1, right);
maitain(id, left, right);
}
void upadate(ll L, ll R, ll d, ll id = 1, ll left = 1, ll right = n) {
if (L <= left && right <= R) {
ll what = 0;
if (d == 0) what = right - left + 1;
lazy_tag[id] = d;
tree[id].l = tree[id].r = tree[id].sum = what;
return ;
}
pushdown(id, left, right);
ll mid = (left + right) >> 1;
if (L <= mid) upadate(L, R, d, ls(id), left, mid);
if (R > mid) upadate(L, R, d, rs(id), mid + 1, right);
maitain(id, left, right);
}
ll query(ll d, ll id = 1, ll left = 1, ll right = n) {
if (left == right) return left;
ll mid = (left + right) >> 1;
if (tree[ls(id)].sum >= d) return query(d, ls(id), left, mid);
else if (tree[ls(id)].r + tree[rs(id)].l >= d) return mid - tree[ls(id)].r + 1;
else return query(d, rs(id), mid + 1, right);
return 0;
}
void solve() {
cin >> n >> m;
build(1, 1, n);
for (int i = 1; i <= m; i++) {
int opt;
cin >> opt;
if (opt == 1) {
int d;
cin >> d;
if (d > tree[1].sum) {
cout << "0\n";
continue;
}
int r = query(d);
cout << r << "\n";
upadate(r, r + d - 1, 1);
} else {
int x, d;
cin >> x >> d;
upadate(x, x + d - 1, 0);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// freopen("hotel.in", "r", stdin);
//// freopen("hotel1.in", "r", stdin);
// freopen("hotel.out", "w", stdout);
int T = 1;
// cin >> T;
while (T--) solve();
return 0;
}
T2 Palindrome Query
哈哈哈,打的暴力比某些Hash得的分还要高
View BF Code
#include<bits/stdc++.h>
using namespace std;
using lf = double;
using ll = long long;
using ull = unsigned long long;
void solve() {
int n, m;
cin >> n >> m;
string str;
cin >> str;
for (int i = 1; i <= m; i++){
int opt;
cin >> opt;
if (opt == 1) {
int x;
char c;
cin >> x >> c;
str[x - 1] = c;
} else {
int l, r;
cin >> l >> r;
int i = l, j = r;
bool flag = 1;
if ((r - l + 1) & 1) {
while (j - i > 1) {
if (str[i - 1] != str[j - 1]) {
cout << "No\n";
flag = 0;
break;
}
i++, j--;
}
if (flag) cout << "Yes\n";
} else {
while (i < j) {
if (str[i - 1] != str[j - 1]) {
cout << "No\n";
flag = 0;
break;
}
i++, j--;
}
if (flag) cout << "Yes\n";
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// freopen("palindrome.in", "r", stdin);
// freopen("palindrome.out", "w", stdout);
int T = 1;
// cin >> T;
while (T--) solve();
return 0;
}
思路:
一眼 Hash
听到有人双Hash 哈希冲突了
还有个单Hash 冲突的(模数用的1333331)
Segment Tree 的结构体设计:
struct node {
long long L, R;
long long len;
}
\(\rarr\) 结构体中 + 的实现:
node operator+(node a,node b){
long long L=a.L*Pow[b.len]%mod+b.L;
L%=mod;
long long R=b.R*Pow[a.len]%mod+a.R;
R%=mod;
int len=a.len+b.len;
return {L,R,len};
}
模数用 1e9 + 7 就好
代码:
View Code
#include<bits/stdc++.h>
using namespace std;
using lf = double;
using ll = long long;
using ull = unsigned long long;
const int maxn = 1e6 + 5;
ll ls(ll i) {
return i << 1;
}
ll rs(ll i) {
return i << 1 | 1;
}
const ll M = 1e9 + 7;
#define mod %
struct node{
ll len;
ll left_hash, right_hash;
node(){}
node(char c) {
len = 1;
left_hash = c - 'a' + 1;
right_hash = c - 'a' + 1;
}
node(ll l, ll lh, ll rh) : len(l), left_hash(lh), right_hash(rh) {}
};
string str;
node tree[maxn << 2];
ll base = 131;
ll Pow[maxn];
node operator+(node a, node b) {
node res;
res.len = a.len + b.len;
res.left_hash = (a.left_hash * Pow[b.len] mod M + b.left_hash) mod M;
res.left_hash = (res.left_hash + M) mod M;
res.right_hash = (b.right_hash * Pow[a.len] mod M + a.right_hash) mod M;
res.right_hash = (res.right_hash + M) mod M;
return res;
}
void maintain(ll id) { // push up
tree[id] = tree[ls(id)] + tree[rs(id)];
}
void build(ll id, ll left_, ll right_) {
if (left_ == right_) {
tree[id] = node(str[left_]);
return;
}
ll mid = (left_ + right_) >> 1;
build(ls(id), left_, mid);
build(rs(id), mid + 1, right_);
maintain(id);
}
void update_range(ll posi, ll id, ll left_, ll right_, char d) {
if (left_ == right_) {
tree[id] = node(d);
return;
}
ll mid = (left_ + right_) >> 1;
if (posi <= mid) update_range(posi, ls(id), left_, mid, d);
else if (posi > mid) update_range(posi, rs(id), mid + 1, right_, d);
maintain(id);
}
node query_range(ll L, ll R, ll id, ll left_, ll right_) {
if (L <= left_ && right_ <= R) return tree[id];
ll mid = (left_ + right_) >> 1;
node res(0, 0, 0);
if (L <= mid) res = res + query_range(L, R, ls(id), left_, mid);
if (R > mid) res = res + query_range(L, R, rs(id), mid + 1, right_);
return res;
}
node query(ll L, ll R, int n) {
return query_range(L, R, 1, 1, n);
}
void update(ll posi, char d, int n) {
update_range(posi, 1, 1, n, d);
}
void solve() {
int n, m;
cin >> n >> m;
cin >> str;
str = " " + str;
Pow[0] = 1;
for (int i = 1; i <= maxn - 5; i++)
Pow[i] = (Pow[i - 1] * base mod M + M) mod M;
build(1, 1, n);
while (m--) {
int oper;
cin >> oper;
if (oper == 1) {
int x;
char c;
cin >> x >> c;
update(x, c, n);
str[x] = c;
} else {
int l, r;
cin >> l >> r;
node res = query(l, r, n);
res.left_hash = (res.left_hash + M) mod M;
res.right_hash = (res.right_hash + M) mod M;
if (res.left_hash == res.right_hash) cout << "Yes\n";
else cout << "No\n";
// for (int i = l; i <= r; i++) cout << str[i];
// cout << "\n";
//
// cout << res.left_hash << " " << res.right_hash << "\n";
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T = 1;
while (T--) solve();
return 0;
}
T3 回转寿司
权值线段树+动态开点
对于每一个 sum[i] ,查询 [sum[i] - r, sum[i] - l] 内的值得个数
View Code
#include <bits/stdc++.h>
using namespace std;
using lf = double;
using ll = long long;
using ull = unsigned long long;
const int maxn = 1e5 + 5;
const ll MIN_X = -1e10;
const ll MAX_X = 1e10;
int n;
ll cnt = 1, root = 1;
ll a[maxn];
ll tree[maxn * 70];
ll ls[maxn * 70], rs[maxn * 70];
void maintain(ll id) { tree[id] = tree[ls[id]] + tree[rs[id]]; }
void update(ll posi, ll d, ll &id, ll left = MIN_X, ll right = MAX_X) {
if (!id)
id = ++cnt;
if (left == right && left == posi) {
tree[id] += d;
return;
}
ll mid = (left + right) >> 1;
if (posi <= mid)
update(posi, d, ls[id], left, mid);
else
update(posi, d, rs[id], mid + 1, right);
maintain(id);
}
ll query(ll L, ll R, ll &id, ll left = MIN_X, ll right = MAX_X) {
if (!id)
id = ++cnt;
if (L <= left && right <= R)
return tree[id];
ll mid = (left + right) >> 1;
ll res = 0;
if (L <= mid)
res += query(L, R, ls[id], left, mid);
if (R > mid)
res += query(L, R, rs[id], mid + 1, right);
return res;
}
void solve() {
int n, l, r;
cin >> n >> l >> r;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
a[i] = a[i - 1] + x;
}
update(0, 1, root);
ll ans = 0;
for (int i = 1; i <= n; i++) {
ans += query(a[i] - r, a[i] - l, root);
update(a[i], 1, root);
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// freopen("T1.in", "r", stdin);
// freopen("T1.out", "w", stdout);
int T = 1;
// cin >> T;
while (T--) solve();
return 0;
}

浙公网安备 33010602011771号