01区间修改

区间修改,一般使用差分代替,

以下这个模板属于区间整体加上一个值,然后单点查询, 相比于线段树操作,代码量少太多了

void BIT_opt(int x, int len, int k){
	for(int i = x; i <= len; i += (i & (-i))){
		tr[i] += k;
	}
	return;
}
 
int query(int x){
	int ans = 0;
	for(int i = x; i; i -= (i & (-i))){
		ans += tr[i];
	}
	return ans;
}
 
solve(){
	vi a(n + 1);
	rep(i, 1, n){
		cin >> a[i];
		BIT_opt(i, n, a[i] - a[i - 1]); //初始化的时候就要差分
	}
	
	while(q --){
		int type; cin >> type;
		if(type == 1){
			int x, y, k; cin >> x >> y >> k;
			BIT_opt(x, n, k);
			if(y < n) BIT_opt(y + 1, n, -k);
		}else{
			int x; cin >> x;
			cout << BIT_query(x) << endl;
		}
	}
	return;
}

02权值树状数组

权值树状数组,就是把数字作为桶, 求逆序对就是其中一种使用
它与普通树状数组的区别,往往在于我们处理一个放进去一个,而不是一下子把全部都放进去

03离线扫描 BIT

将查询全都储存下来,按照 r 排序, 然后处理,实际上就是离线排序 + 权值树状数组

04二维偏序/BIT

问题类型

单点查询, 对于每个 , 有多少个 , 满足 ,

问题区分

对于交换问题,需要区分,任意两个数字交换还是必须相邻数字交换时所需的交换次数

交换规则交换次数计算方法
相邻数字交换逆序对个数树状数组/归并排序
任意两个数字交换 - 置换循环节个数找环
注:对于第二种情况,可以看置换群的笔记

05二维BIT

区间操作,区间查询
下面写的较为笼统,实际操作的时候 + 1 , - 1 的地方记得注意边界

vii tr1(N, vi(N)), tr2(N, vi(N)), tr3(N, vi(N)), tr4(N, vi(N));
 
void BIT_opt(int x, int y, int k){
	for(int i = x; i <= n; i ++ (i & (-i))){
		for(int j = y; j <= n; j += (j & (-j))){
			tr1[i][j] += k;
			tr2[i][j] += x * k;
			tr3[i][j] += y * k;
			tr4[i][j] += x * y * k;
		}
	}
}
 
int BIT_query(int x, int y){
	int ans = 0;
	for(int i = x; i; i -= (i & (-i))){
		for(int j = y; j; j -= (i & (-i))){
			ans += tr1[i][j] * (x + 1) * (y + 1) - tr2[i][j] * (y + 1) - tr3[i][j] * (x + 1) + tr4[i][j];
		}
	}
	return ans;
}
 
void solve(){
	while(q --){
		int type; cin >> type;
		if(type){
			int x1, y1, x2, y2, k; cin >> x1 >> y1 >> x2 >> y2 >> k;
			BIT_opt(x1, y2, k);
			BIT_opt(x2 + 1, y1, - k);
			BIT_opt(x1, y2 + 1, - k);
			BIT_opt(x2 + 1, y2 + 1, k);
		}else{
			int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
			cout << BIT_query(x2, y2) + BIT_query(x1 - 1, y1 - 1) - BIT_query(x1 - 1, y2) - BIT_query(x2, y1 - 1) << endl;
		}
	}
}