数据结构专题
线段树
一维线段树
区间第 大
学了平衡树的大佬们乐坏了,我一个 都不会打的蒟蒻咋办捏?
线段树维护。还挺好维护的。
问的是区间第 大,那就维护一个区间最小值。在树外先二分答案 ,用线段树去 ,统计一下比 大的有几个。维护区间最小值是为了遇到最小值都比大的就用不递归过去了,直接加上右边元素数量。
区间染色·查询是否存在某颜色
如果就那种颜色数量 , 的,直接二进制压缩就行了,到时候对应的颜色有没有直接一 就行了。
的我不清楚,也许__int128
可以?
二维线段树
咕了
树状数组
一维树状数组
区修单查·差分
说一下差分。这个东西一直没搞明白,后来也没再想。
对于树状数组维护区间更改、单点查询而言,差分是很好的一个方法。
设当前要使区间 的数值加上 ,那么就使位置的数值 ,在 位置的数值 。查询的时候只需要查询当前点的前缀和。
考虑为什么这样更新。
如果使区间 的数值加上 ,那么 与 的差值就会增加了 ,而 与 的差值就会减少 。故。
用代码写出来就是这样:
Code
inline void Update(int k, int w){
while (k <= n)
C[w] += k, k += lowbit(k);
}
inline void Range_Update(int l, int r, int w){
Update(l, w), Update(r+1, -w);
}
inline int Query(int k){
int res(0);
while (k != 0)
res += C[k], k -= lowbit(k);
return res;
}
区修区查
区修区查你闲的没事用树状数组干嘛。自己去写个线段树不就行了。(doge
二维树状数组
单修区查
这种情况下还是蛮简单的。
首先先整一个前缀和sum[X][Y]
,然后去除掉一些部分sum[x-1][Y]
和sum[X][y-1]
,最后再容斥一下加上sum[x-1][y-1]
这个画图理解就行了,注意画图分清楚画的是点还是块。
#include <iostream>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 305
#define COLOR 105
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}
int n, m, Q;
int s[N][N];
int C[N][N][COLOR];
#define lowbit(x) ((x) & (-(x)))
inline void Update(int x, int y, int c, int w){
int ago(y);
while (x <= n){
y = ago;
while (y <= m){
C[x][y][c] += w;
y += lowbit(y);
// cerr << "胡萝贝" << x << _ << y << _ << c << '\n';
}
x += lowbit(x);
}
// Dl, Dl, Dl;
}
inline int Query(int x, int y, int c){
int res(0), ago(y);
while (x != 0){
y = ago;
while (y != 0){
res += C[x][y][c];
y -= lowbit(y);
}
x -= lowbit(x);
}
return res;
}
inline void work(){
cin >> n >> m;
for (re i = 1 ; i <= n ; ++ i){
for (re j = 1 ; j <= m ; ++ j){
cin >> s[i][j];
Update(i, j, s[i][j], 1);
// cerr << "First " << i << _ << j << _ << C[2][2][1] << '\n';
}
}
cin >> Q;
int opt, x, y, X, Y, c;
while (Q --){
cin >> opt;
if (opt == 1){
cin >> x >> y >> c;
Update(x, y, s[x][y], -1);
s[x][y] = c;
Update(x, y, c, 1);
}
else {
cin >> x >> X >> y >> Y >> c;
cout << Query(X, Y, c)-Query(x-1, Y, c)-Query(X, y-1, c)+Query(x-1, y-1, c) << '\n';
// cerr << "孩抵小纵队" << Query(X, Y, c) << _ << Query(x-1, Y, c) << _ << Query(X, y-1, c) << _ << Query(x-1, y-1, c) << '\n';
}
}
}
// #define IXINGMY
char_phi main(){
#ifdef IXINGMY
FBI_OPENTHEDOOR(a);
#endif
Fastio_setup();
work();
return GMY;
}
区修单查·二维差分
这个还是用差分,不过是二维差分,前缀和sum[x][y]
即为点 的值。
考虑如何维护差分。
在二维平面中,差分数组d[x][y]
的定义为:a[x][y]
与a[x-1][y]+a[x][y-1]-a[x-1][y-1]
的差。
设初始矩阵:
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
对正中间的 矩阵加上一个 后差分数组的变为:
0 0 0 0 0
0 x 0 0 -x
0 0 0 0 0
0 0 0 0 0
0 -x 0 0 x
这个直接手模出来。
查询就是直接的查询了。更新的话还是需要对着差分数组更新的。
具体地:
inline void Range_Update(int x, int y, int X, int Y, int w){
Update(x, y, w), Update(x, Y+1, -w), Update(X+1, y, -w), Update(X+1, Y+1, w);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人