数据结构专题

Updating!

线段树

一维线段树

Xenny

区间第 k

学了平衡树的大佬们乐坏了,我一个 splay 都不会打的蒟蒻咋办捏?

线段树维护。还挺好维护的。

问的是区间第 k 大,那就维护一个区间最小值。在树外先二分答案 mid,用线段树去 check,统计一下比 mid 大的有几个。维护区间最小值是为了遇到最小值都比mid大的就用不递归过去了,直接加上右边元素数量。

区间染色·查询是否存在某颜色

如果就那种颜色数量 3060 的,直接二进制压缩就行了,到时候对应的颜色有没有直接一 & 就行了。

100 的我不清楚,也许__int128可以?

二维线段树

咕了

树状数组

一维树状数组

嗯嗯还是Xenny

胡小兔

区修单查·差分

说一下差分。这个东西一直没搞明白,后来也没再想。

对于树状数组维护区间更改、单点查询而言,差分是很好的一个方法。

设当前要使区间 [l,r] 的数值加上 x,那么就使l位置的数值 +=x,在 r+1 位置的数值 -=x。查询的时候只需要查询当前点的前缀和。

考虑为什么这样更新

如果使区间 [l,r] 的数值加上 x,那么 ll1 的差值就会增加了 x,而 r+1r 的差值就会减少 x。故。

用代码写出来就是这样:

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]

这个画图理解就行了,注意画图分清楚画的是点还是块。

板子题

Code
#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]即为点 (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

对正中间的 3×3 矩阵加上一个 5 后差分数组的变为:

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);
}
posted @   char_phi  阅读(157)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示