20260112 - 树状数组

前言

第一次 Rank 1 呢!

树状数组

基本概念

树状数组是一种高效维护动态前缀和(动态前缀最值)的数据结构,支持:

  • 单点更新 \(O(\log_2n)\)
  • 前缀求和 \(O(\log_2n)\)

空间复杂度\(O(n)\)

优势

  • 代码简洁( \(<20\)行代码)
  • 常数因子小(相比线段树)
  • 内存效率高(相比线段树)

劣势

  • 无法操作区间翻转等复杂操作。
  • 处理单点修改 \(\max\) 需要 \(\log_2^2 n\) 的时间,不如线段树。
  • 没有分块灵活。

结构图

       8
    /     \
   4       12
  / \     / \
 2   6   10  14
/ \ / \ / \ / \
1 3 5 7 9 11 13 15

思想

可以知道,任意一个数都可以表示为至多 \(\log_2\)\(2\) 的幂的和,这样子就可以让单点区间修改从 \(O(n)\) 优化到 \(O(\log_2n)\)

快速幂,倍增都是这样的思想。

lowbit

这是树状数组聪明的地方!

在树状数组中,我们利用 lowbit(二进制表示中最小的1)来实现这种操作。

6 的 lowbit 是 2,8 的 lowbit 是 8,可以用 x & -x 来快速计算。

树状数组封装代码

template <class T> struct BIT {
	vector <T> c;
	int n;
    BIT (int n_) {
        n = n_;
        c.resize(n + 1);
    }
	int lowbit (int x) {
		return x & -x;
	}
	void update (int x, T val) {
		while (x <= n) {
			c[x] += val;
			x += lowbit(x);
		}
	}
	T query (int x) {
		T res = 0;
		while (x > 0) {
			res += c[x];
			x -= lowbit(x);
		}
		return res;
	}
	T query (int l, int r) {
		return query(r) - query(l - 1);
	}
	void build (int n_, T a[]) {
		n = n_;
		c.resize(n + 1);
		for (int i = 1; i <= n; i++)
			update(i, a[i]);
	}
	void build (int n_) {
		n = n_;
		c.resize(n + 1);
	}
	~BIT() {
		c.clear();
	}
};

使用方法

vector 很像,使用时直接用 BIT <T> bit,T 代指数据类型(int, long long 等)。

例题

A - 求区间和

前缀和?分块?莫队?不不不,是线段树树状数组!

用上面的搞一搞就好了!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 5e5 + 7;
constexpr int P = 998244353;
template<class T> struct BIT {
	vector <T> c;
	int n;
	int lowbit (int x) {
		return x & -x;
	}
	void update (int x, T val) {
		while (x <= n) {
			c[x] += val;
			x += lowbit(x);
		}
	}
	T query (int x) {
		T res = 0;
		while (x > 0) {
			res += c[x];
			x -= lowbit(x);
		}
		return res;
	}
	T query (int l, int r) {
		return query(r) - query(l - 1);
	}
	void build (int n_, T a[]) {
		n = n_;
		c.resize(n + 1);
		for (int i = 1; i <= n; i++)
			update(i, a[i]);
	}
	void build (int n_) {
		n = n_;
		c.resize(n + 1);
	}
	~BIT() {
		c.clear();
	}
};
int n, m, a[N];
void solve() {
	cin >> n;
	rep (i, 1, n) cin >> a[i];
	BIT <int> bit;
	bit.build(n, a);
	cin >> m;
	while (m--) {
		int op, l, r;
		cin >> l >> r;
		cout << bit.query(l, r) << endl;
	}
}
int main() {
	int oT_To = 1;
	// cin >> oT_To;
	while (oT_To--) solve();
	return 0;
}
B - 树状数组 1

树状数组模版!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 5e5 + 7;
constexpr int P = 998244353;
template<class T> struct BIT {
	vector <T> c;
	int n;
	int lowbit (int x) {
		return x & -x;
	}
	void update (int x, T val) {
		while (x <= n) {
			c[x] += val;
			x += lowbit(x);
		}
	}
	T query (int x) {
		T res = 0;
		while (x > 0) {
			res += c[x];
			x -= lowbit(x);
		}
		return res;
	}
	T query (int l, int r) {
		return query(r) - query(l - 1);
	}
	void build (int n_, T a[]) {
		n = n_;
		c.resize(n + 1);
		for (int i = 1; i <= n; i++)
			update(i, a[i]);
	}
	void build (int n_) {
		n = n_;
		c.resize(n + 1);
	}
	~BIT() {
		c.clear();
	}
};
int n, m, a[N];
void solve() {
	cin >> n >> m;
	rep (i, 1, n) cin >> a[i];
	BIT <int> bit;
	bit.build(n, a);
	while (m--) {
		int op, l, r;
		cin >> op >> l >> r;
		if (op == 1) {
			bit.update(l, r);
		}else {
			cout << bit.query(l, r) << endl;
		}
	}
}
int main() {
	int oT_To = 1;
	// cin >> oT_To;
	while (oT_To--) solve();
	return 0;
}
C - 逆序对

使用权值树状数组,维护每个数出现的次数,

在统计第 \(i\) 个数 \(a[i]\) 对逆序对的贡献时,查询大于 \(a[i]\) 的数有多少个即可,

然后把 \(a[i]\) 的出现次数 \(+1\)

注意:离散化

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 5e5 + 7;
constexpr int P = 998244353;
template<class T> struct BIT {
	vector <T> c;
	int n;
	int lowbit (int x) {
		return x & -x;
	}
	void update (int x, T val) {
		while (x <= n) {
			c[x] += val;
			x += lowbit(x);
		}
	}
	T query (int x) {
		T res = 0;
		while (x > 0) {
			res += c[x];
			x -= lowbit(x);
		}
		return res;
	}
	T query (int l, int r) {
		return query(r) - query(l - 1);
	}
	void build (int n_, T a[]) {
		n = n_;
		c.resize(n + 1);
		for (int i = 1; i <= n; i++)
			update(i, a[i]);
	}
	void build (int n_) {
		n = n_;
		c.resize(n + 1);
	}
	~BIT() {
		c.clear();
	}
};
int n, a[N], b[N];
map <int, int> mp;
void solve() {
  BIT <ll> bit;
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    b[i] = a[i];
  }
  sort(b + 1, b + n + 1);
  int idx = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1; i <= idx; i++)
		mp[b[i]] = i;
  bit.build(n);
  ll ans = 0;
  for (int i = 1; i <= n; i++) {
    int id = mp[a[i]];
    ans += bit.query(id + 1, idx);
    bit.update(id, 1);
  }
  cout << ans << endl;
}
int main() {
  int oT_To = 1;
  // cin >> oT_To;
  while (oT_To--) solve();
  return 0;
}
D - 树状数组 2

用树状数组维护原序列的差分数组,差分数组的前缀和就是原序列的值。

所以 \([l, r]\) 区间整体 \(+x\),就是在树状数组上的 \(l\) 位置 \(+x\)\(r+1\) 位置 \(-x\)

查询原序列的第 \(i\) 个元素,就是在树状数组上求前 \(i\) 项之和。

注意:如果 \(r + 1\) 大于 \(n\),则不减,因为会越界!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 5e5 + 7;
constexpr int P = 998244353;
template<class T> struct BIT {
	vector <T> c;
	int n;
	int lowbit (int x) {
		return x & -x;
	}
	void update (int x, T val) {
		while (x <= n) {
			c[x] += val;
			x += lowbit(x);
		}
	}
	T query (int x) {
		T res = 0;
		while (x > 0) {
			res += c[x];
			x -= lowbit(x);
		}
		return res;
	}
	T query (int l, int r) {
		return query(r) - query(l - 1);
	}
	void build (int n_, T a[]) {
		n = n_;
		c.resize(n + 1);
		for (int i = 1; i <= n; i++)
			update(i, a[i]);
	}
	void build (int n_) {
		n = n_;
		c.resize(n + 1);
	}
	~BIT() {
		c.clear();
	}
};
int n, m, a[N];
void solve() {
	cin >> n >> m;
	rep (i, 1, n) cin >> a[i];
	BIT <int> bit;
	bit.build(n);
	while (m--) {
		int op, l, r, k;
		cin >> op >> l;
		if (op == 1) {
			cin >> r >> k;
			bit.update(l, k);
			if (r + 1 <= n) bit.update(r + 1, -k);
		}else {
			cout << bit.query(1, l) + a[l] << endl;
		}
	}
}
int main() {
	int oT_To = 1;
	// cin >> oT_To;
	while (oT_To--) solve();
	return 0;
}
E - 中位数

方法一:堆。。。

方法二:树状数组。。。

又是一个权值树状数组,二分去查询前缀的值是否为 \(\lfloor\dfrac{n}{2} \rfloor + 1\),二分作死请见 ST 表总结!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 1e5 + 7;
constexpr int P = 998244353;
template<class T> struct BIT {
	vector <T> c;
	int n;
	int lowbit (int x) {
		return x & -x;
	}
	void update (int x, T val) {
		while (x <= n) {
			c[x] += val;
			x += lowbit(x);
		}
	}
	T query (int x) {
		T res = 0;
		while (x > 0) {
			res += c[x];
			x -= lowbit(x);
		}
		return res;
	}
	T query (int l, int r) {
		return query(r) - query(l - 1);
	}
	void build (int n_, T a[]) {
		n = n_;
		c.resize(n + 1);
		for (int i = 1; i <= n; i++)
			update(i, a[i]);
	}
	void build (int n_) {
		n = n_;
		c.resize(n + 1);
	}
	~BIT() {
		c.clear();
	}
};
int n, a[N], b[N], val[N];
map <int, int> mp;
void solve() {
  BIT <int> bit;
  cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		b[i] = a[i];
	} 
  bit.build(n);
	sort(b + 1, b + n + 1);
  int idx = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1;i <= idx;i++)
		mp[b[i]] = i, val[i] = b[i];
	for (int i = 1; i <= n; i++) {
    int id = mp[a[i]];
		bit.update(id, 1);
		if (i & 1) {
			int l = 0, r = idx + 1;
			while(l + 1 < r) {
				int mid = (l + r) / 2;
				if (bit.query(1, mid) >= i / 2 + 1) {
					r = mid;
				}else {
					l = mid;
				}
			}
      cout << val[r] << endl;
		}
	}
}
int main() {
  int oT_To = 1;
  // cin >> oT_To;
  while (oT_To--) solve();
  return 0;
}
F - MooFest G

有绝对值,很讨厌,遇到这种题目,直接分类讨论:

  • \(a_i - a_j > 0\)
  • \(a_i - a_j \le 0\)

在统计求和即可!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 2e4 + 7, MAXN = 2e4;
constexpr int P = 998244353;
int n;
vector <pair <int, int>> ve;
template<class T> struct BIT {
	vector <T> c;
	int n;
	int lowbit (int x) {
		return x & -x;
	}
	void update (int x, T val) {
		while (x <= n) {
			c[x] += val;
			x += lowbit(x);
		}
	}
	T query (int x) {
		T res = 0;
		while (x > 0) {
			res += c[x];
			x -= lowbit(x);
		}
		return res;
	}
	T query (int l, int r) {
		return query(r) - query(l - 1);
	}
	void build (int n_, T a[]) {
		n = n_;
		c.resize(n + 1);
		for (int i = 1; i <= n; i++)
			update(i, a[i]);
	}
	void build (int n_) {
		n = n_;
		c.resize(n + 1);
	}
	~BIT() {
		c.clear();
	}
};
void solve() {
  BIT <ll> t1, t2;
  cin >> n;
  ve.resize(n);
  t1.build(MAXN);
  t2.build(MAXN);
  for (int i = 0; i < n; i++) cin >> ve[i].first >> ve[i].second;
  sort(ve.begin(), ve.end());
  ll ans = 0, cnt = 0;
  for (auto [v, x] : ve) {
    // cout << v << " " << x << endl;
    ll res = v * ((x * t1.query(1, x)) - t2.query(1, x));
    ll ret = v * (t2.query(1, MAXN) - t2.query(1, x) - (cnt - t1.query(1, x)) * x);
    ans += res + ret;
    ++cnt;
    // cout << (t1.query(1, x))  << " " << ret << endl;
    t1.update(x, 1);
    t2.update(x, x);
  }
  cout << ans << endl;
} 
int main() {
  int oT_To = 1;
  // cin >> oT_To;
  while (oT_To--) solve();
  return 0;
}
G - Balanced Lineup G

老师曾没讲的树状数组求区间最值!

老师上课时,yxc 问老师,是不是只用数据结构就行,老师没说话,yxc 以为默认了,所以就有了线段树做法!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 1e6 + 7;
constexpr int P = 998244353;
int n, q, a[N], t[N * 4], f[N * 4];
void build(int id, int l, int r) {
  if (l == r) {
    t[id] = a[l];
    f[id] = a[l];
    return;
  }
  int mid = l + (r - l) / 2;
  build(id * 2, l, mid);
  build(id * 2 + 1, mid + 1, r);
  t[id] = max(t[id * 2], t[id * 2 + 1]);
  f[id] = min(f[id * 2], f[id * 2 + 1]);
}
int calc(int id, int l, int r, int ql, int qr) {
  if (r < ql || l > qr) return INT_MAX;
  if (ql <= l && r <= qr) return f[id];
  int mid = l + (r - l) / 2;
  return min(calc(id * 2, l, mid, ql, qr), calc(id * 2 + 1, mid + 1, r, ql, qr));
}
int query(int id, int l, int r, int ql, int qr) {
  if (r < ql || l > qr) return 0;
  if (ql <= l && r <= qr) return t[id];
  int mid = l + (r - l) / 2;
  return max(query(id * 2, l, mid, ql, qr) ,query(id * 2 + 1, mid + 1, r, ql, qr));
}
void solve() {
  memset(f, 0x3f, sizeof(f));
  cin >> n >> q;
  rep (i, 1, n) cin >> a[i];
  build(1, 1, n);
  while (q--) {
    int x, y;
    cin >> x >> y;
    cout << query(1, 1, n, x, y) - calc(1, 1, n, x, y) << endl;
  }
}
int main() {
  int oT_To = 1;
  // cin >> oT_To;
  while (oT_To--) solve();
  return 0;
}
H - LIT-Letters

这可能是常数最大的做法吧!

我们因为要记录每一个字母出现的位置,当时不知道为什么,写了一个常数倍增的做法,但好像很好懂!

剩下的就是逆序对了!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 1e6 + 7;
constexpr int P = 998244353;
template<class T> struct BIT {
	vector <T> c;
	int n;
	int lowbit (int x) {
		return x & -x;
	}
	void update (int x, T val) {
		while (x <= n) {
			c[x] += val;
			x += lowbit(x);
		}
	}
	T query (int x) {
		T res = 0;
		while (x > 0) {
			res += c[x];
			x -= lowbit(x);
		}
		return res;
	}
	T query (int l, int r) {
		return query(r) - query(l - 1);
	}
	void build (int n_, T a[]) {
		n = n_;
		c.resize(n + 1);
		for (int i = 1; i <= n; i++)
			update(i, a[i]);
	}
	void build (int n_) {
		n = n_;
		c.resize(n + 1);
	}
	~BIT() {
		c.clear();
	}
};
int n, a[N], b[N];
map <int, int> mp;
void solve() {
  BIT <ll> bit;
  cin >> n;
  array <deque<int>, CHAR_MAX> cnt; // 常数大大大
  string aa, bb;
  cin >> aa >> bb;
  for (int i = 1; i <= n; i++) {
    cnt[aa[i - 1]].push_back(i);
  }
  rep (i, 1, n) {
    a[i] = cnt[bb[i - 1]].front();
    cnt[bb[i - 1]].pop_front();
    b[i] = a[i];
  }
  sort(b + 1, b + n + 1);
  int idx = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1; i <= idx; i++)
		mp[b[i]] = i;
  bit.build(n);
  ll ans = 0;
  for (int i = 1; i <= n; i++) {
    int id = mp[a[i]];
    ans += bit.query(id + 1, idx);
    bit.update(id, 1);
  }
  cout << ans << endl;
}
int main() {
  int oT_To = 1;
  // cin >> oT_To;
  while (oT_To--) solve();
  return 0;
}
I - Sound 静音问题

和 G 一样,被 yxc 的默认给坑了!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
typedef pair<int, int> PII;
constexpr int N = 1e6 + 7;
constexpr int P = 998244353;
int n, m, c, a[N], t[N * 4], f[N * 4];
void build(int id, int l, int r) {
  if (l == r) {
    t[id] = a[l];
    f[id] = a[l];
    return;
  }
  int mid = l + (r - l) / 2;
  build(id * 2, l, mid);
  build(id * 2 + 1, mid + 1, r);
  t[id] = max(t[id * 2], t[id * 2 + 1]);
  f[id] = min(f[id * 2], f[id * 2 + 1]);
}
int calc(int id, int l, int r, int ql, int qr) {
  if (r < ql || l > qr) return INT_MAX;
  if (ql <= l && r <= qr) return f[id];
  int mid = l + (r - l) / 2;
  return min(calc(id * 2, l, mid, ql, qr), calc(id * 2 + 1, mid + 1, r, ql, qr));
}
int query(int id, int l, int r, int ql, int qr) {
  if (r < ql || l > qr) return 0;
  if (ql <= l && r <= qr) return t[id];
  int mid = l + (r - l) / 2;
  return max(query(id * 2, l, mid, ql, qr) ,query(id * 2 + 1, mid + 1, r, ql, qr));
}
void solve() {
  memset(f, 0x3f, sizeof(f));
  cin >> n >> m >> c;
  rep (i, 1, n) cin >> a[i];
  build(1, 1, n);
  int cnt = 0;
  rep (i, 1, n - m + 1) {
    if (query(1, 1, n, i, i + m - 1) - calc(1, 1, n, i, i + m - 1) <= c) {
      ++cnt;
      cout << i << '\n';
    }
  }
  if (!cnt) cout << "NONE" << endl;
}
int main() {
  int oT_To = 1;
  // cin >> oT_To;
  while (oT_To--) solve();
  return 0;
}
J - Mishka and Interesting sum

嗯,CF 又炸了!

原来是求出现偶数次的单个数的异或和

通过凑样例可以发现,答案是求区间内不同数的异或和与区间异或和的异或和

那么现在问题就变为维护区间内不同的数的异或和

这题数据范围的话,需要离散化

这个好像离线二维数点的题目,但是,如果出现过,就直接删除。

本来想用指针的,发现变量的生命是如此的短暂,导致了指向了一个空指针!

#include <bits/stdc++.h>

using namespace std;
#define rep(i, x, y) for (int i = (x); i <= (y); i++)
#define per(i, x, y) for (int i = (x) ;i >= (y); i--)
#define ll long long
#define ull unsigned long long
#define db double
#define sz(x) ((int)x.size())
#define inf (1 << 30)
#define pb push_back
#define lowbit(x) (x & -x)
typedef pair<int, int> PII;
constexpr int N = 1e6 + 7;
constexpr int P = 998244353;
int n, m, a[N], b[N], s[N], c[N]; 
int head[N], pre[N];
struct Node {
  int l, r, id;
};
vector <Node> ve;
void add(int x, int val) {
  while (x <= n) {
    c[x] ^= val;
    x += lowbit(x);
  }
}
int query(int x) {
  int res = 0;
  while (x) {
    res ^= c[x];
    x -= lowbit(x);
  }
  return res;
}
void solve() {
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    b[i] = a[i];
  }
  sort(b + 1, b + n + 1);
  int idx = unique(b + 1, b + n + 1) - b - 1;
  for (int i = 1; i <= idx; i++) head[i] = 0;
  for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + idx + 1, a[i]) - b;
  for (int i = 1; i <= n; i++) {
    s[i] = s[i - 1] ^ b[a[i]];
    // if (head[a[i]])
      // pre[i] = *head[a[i]];
    // else  
      // pre[i] = 0;
    // head[a[i]] = &i;
    pre[i] = head[a[i]];
    head[a[i]] = i;
  }
  cin >> m;
  for (int i = 1; i <= m; i++) {
    int l, r;
    cin >> l >> r;
    ve.pb({l, r, i});
  } 
  sort(ve.begin(), ve.end(), [&](const Node &x, const Node &y) {
    return x.r < y.r;
  });
  int now = 1;
  vector <int> ans(m);
  for (auto [l, r, id] : ve) {
    while (now <= r) {
      if (pre[now]) add(pre[now], b[a[now]]);
      add(now, b[a[now]]);
      now++;
    }
    ans[id - 1] = (query(r) ^ query(l - 1)) ^ (s[r] ^ s[l - 1]);
  }
  for (auto v : ans) cout << v << endl;
}
int main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  int oT_To = 1;
  // cin >> oT_To;
  while (oT_To--) solve();
  return 0;
}

警示后人:没事不要用指针,看着不好用,实际上也不是很好用!(除非闲的没事干)

后记

树状数组可以解决带修前缀问题,是一个很好用但局限性很高的数据结够。总而言之,码量完胜线段树,适用场景被线段树吊打!

posted @ 2026-01-13 21:10  AKCoder  阅读(18)  评论(0)    收藏  举报