二维树状数组

二维树状数组

二维较一维只多了一层循环。

对于每个位置 \((x_0,y_0)\),管辖的区间为 \(\begin{cases}x:&(x_0-\operatorname{lowbit}(x_0),x_0]\\y:&(y_0-\operatorname{lowbit}(y_0),y_0]\end{cases}\)

修改 \((x_0,y_0)\):对于每个管辖 \(x_0\) 的行,在每个管辖 \(y_0\) 的列上修改。

void add(int x,int y,T cnt){
	for(int i=x;i<n;i+=lowbit(i))
		for(int j=y;j<m;j+=((j)&-(j)))
			t[i][j]+=cnt;
}

__builtin_ffs 是什么鬼东西,返回 __builtin_ctz+1,我一直以为是 lowbit

查询 \(\operatorname{op}_{i=1}^{x_0}\operatorname{op}_{j=1}^{y_0}(i,j)\):直接两轮暴力跳就好。

T query(int x,int y){
	T ans=0;
	for(int i=x;i>0;i-=lowbit(i))
		for(int j=y;j>0;j-=lowbit(j))
			ans+=t[i][j];
	return ans;
}

查询区间加个差分。

T query(int lx,int ly,int rx,int ry){
	return query(rx,ry)-query(rx,ly-1)-query(lx-1,ry)+query(lx-1,ly-1);
}

都是 \(O(\log nm)\)

上完整代码:

template<typename T=ll>class fenwick{
	vector<vector<T>>t;
	int n,m;
public:
	fenwick(int _n,int _m):n(_n+1),m(_m+1){t.assign(n,vector<T>(m));}
	fenwick(int _n,int _m,T):n(_n+1),m(_m+1){t.assign(n,vector<T>(m));}// 这玩意适合C++17模板类型推导。
#define lowbit(x)(x&-(x))
	void add(int x,int y,T cnt){
		for(int i=x;i<n;i+=lowbit(i))
			for(int j=y;j<m;j+=((j)&-(j)))
				t[i][j]+=cnt;
	}
	T query(int x,int y){
		T ans=0;
		for(int i=x;i>0;i-=lowbit(i))
			for(int j=y;j>0;j-=lowbit(j))
				ans+=t[i][j];
		return ans;
	}
	T query(int lx,int ly,int rx,int ry){
		return query(rx,ry)-query(rx,ly-1)-query(lx-1,ry)+query(lx-1,ly-1);
	}
#undef lowbit
};

直接上差分:

template<typename T=ll>class fenwick2{
	fenwick<T>fnw;
public:
	fenwick2(int n,int m):fnw(n,m){}
	void add(int lx,int ly,int rx,int ry,T cnt){
		fnw.add(lx,ly,cnt);
		fnw.add(lx,ry+1,-cnt);
		fnw.add(rx+1,ly,-cnt);
		fnw.add(rx+1,ry+1,cnt);
	}
	T query(int x,int y){
		return fnw.query(x,y);
	}
};

接下来上肝帝行为:

区间修区间查!

考虑树状数组的好搭档差分。

\(a\) 的从差分为 \(d\),即 \(\operatorname{op}_{i=1}^x\operatorname{op}_{j=1}^yd_{i,j}=a_{x,y}\)

下面讨论 \(\operatorname{op}\)\(\sum\) 的情况。

考虑区间求值。

\(f(x,y)=\sum_{i=1}^x\sum_{j=1}^ya_{i,j}\)

\[\sum_{i=x}^y\sum_{j=z}^wa_{i,j}=f(y,w)-f(x-1,w)-f(y,z-1)+f(x-1,z-1) \]

\[\begin{aligned} &f(x,y)\\ =&\sum_{i=1}^x\sum_{j=1}^ya_{i,j}\\ =&\sum_{i=1}^x\sum_{j=1}^y\sum_{k=1}^i\sum_{l=1}^jd_{k,l}\\ \end{aligned} \]

其中 \(d_{1,1}\) 求和 \(xy\) 次,\(d_{1,2}\) 求和 \(x(y-1)\) 次,因为当 \(j=1\)\(d_{1,2}\) 不被求和,\(d_{1,3}\) 求和 \(x(y-2)\) 次,因为当 \(j=1,2\)\(d_{1,3}\) 不被求和;\(d_{2,1}\) 求和 \((x-1)y\) 次,因为当 \(i=1\)\(d_{2,1}\) 不被求和。

所以总结规律:\(d_{k,l}\) 被求和 \((x+1-k)(y+1-l)\) 次,因为当 \(i=1\sim k-1\)\(j=1\sim l-1\)\(d_{k,l}\) 不被求和。

所以优化了两层循环。

\[\begin{aligned} &f(x,y)\\ =&\sum_{i=1}^x\sum_{j=1}^yd_{i,j}(x+1-i)(y+1-j)\\ =&\sum_{i=1}^x\sum_{j=1}^yd_{i,j}((x+1)(y+1)-(x+1)j-(y+1)i+ij)\\ =&\sum_{i=1}^x\sum_{j=1}^yd_{i,j}(x+1)(y+1)\\ &-\sum_{i=1}^x\sum_{j=1}^yd_{i,j}(x+1)j\\ &-\sum_{i=1}^x\sum_{j=1}^yd_{i,j}(y+1)i\\ &+\sum_{i=1}^x\sum_{j=1}^yd_{i,j}ij\\ =&(x+1)(y+1)\sum_{i=1}^x\sum_{j=1}^yd_{i,j}\\ &-(x+1)\sum_{i=1}^x\sum_{j=1}^yjd_{i,j}\\ &-(y+1)\sum_{i=1}^x\sum_{j=1}^yid_{i,j}\\ &+\sum_{i=1}^x\sum_{j=1}^yijd_{i,j}\\ \end{aligned} \]

好的。

一维怎么写的?

auto add=[&](long long l,long long r,long long x){
	b.add(l,x),b.add(r+1,-x);
	bi.add(l,x*l),bi.add(r+1,-x*(r+1));
};
auto query=[&](long long k){
	return b.query(k)*(k+1)-bi.query(k);
};
auto qr=[&](long long l,long long r){
	return query(r)-query(l-1);
};

维护了两个树状数组。

那么二维的维护四个树状数组。

开始抽象。

template<typename T=ll>class segtree{// Not Segment Tree but Fenwick Tree!
	fenwick<T>t0,t1,t2,t3;
	T pre(int x,int y){
		return	x*y*t0.query(x,y)
			-	x*  t1.query(x,y)
			-	  y*t2.query(x,y)
			+		t3.query(x,y);
	};
public:
	segtree(int n,int m):t0(n+5,m+5),t1(n+5,m+5),t2(n+5,m+5),t3(n+5,m+5){}
	segtree(int n,int m,T):t0(n+5,m+5),t1(n+5,m+5),t2(n+5,m+5),t3(n+5,m+5){}
	void add(int lx,int ly,int rx,int ry,T cnt){
		t0.add(lx,ly,cnt);t0.add(lx,ry+1,-cnt);t0.add(rx+1,ly,-cnt);t0.add(rx+1,ry+1,cnt);
		t1.add(lx,ly,cnt*ly);t1.add(lx,ry+1,-cnt*(ry+1));t1.add(rx+1,ly,-cnt*ly);t1.add(rx+1,ry+1,cnt*(ry+1));
		t2.add(lx,ly,cnt*lx);t2.add(lx,ry+1,-cnt*lx);t2.add(rx+1,ly,-cnt*(rx+1));t2.add(rx+1,ry+1,cnt*(rx+1));
		t3.add(lx,ly,cnt*lx*ly);t3.add(lx,ry+1,-cnt*lx*(ry+1));t3.add(rx+1,ly,-cnt*(rx+1)*ly);t3.add(rx+1,ry+1,cnt*(rx+1)*(ry+1));
	};
	T query(int lx,int ly,int rx,int ry){
		++lx,++ly,++rx,++ry;
		return pre(rx,ry)-pre(rx,ly-1)-pre(lx-1,ry)+pre(lx-1,ly-1);
	};
};

\(O(\log nm)\)

\(16\) 常大数。

看题

没什么可看的,都是板子。

F

如果两个位置在同一层的话就 \(\texttt{Yes}\),否则 \(\texttt{No}\)

如何判同一层?每加一层就给这一层加个随机数(减的时候当然要减掉这个随机数,为了避免溢出,使用 ull 自然溢出)如果位置的层级相同的话直接加。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;
template<typename T=ll>class fenwick{
	vector<vector<T>>t;
	int n,m;
public:
	fenwick(int _n,int _m):n(_n+1),m(_m+1){t.assign(n,vector<T>(m));}
#define lowbit(x)(x&-(x))
	void add(int x,int y,T cnt){
		for(int i=x;i<n;i+=lowbit(i))
			for(int j=y;j<m;j+=((j)&-(j)))
				t[i][j]+=cnt;
	}
	T query(int x,int y){
		T ans=0;
		for(int i=x;i>0;i-=lowbit(i))
			for(int j=y;j>0;j-=lowbit(j))
				ans+=t[i][j];
		return ans;
	}
	T query(int lx,int ly,int rx,int ry){
		return query(rx,ry)-query(rx,ly-1)-query(lx-1,ry)+query(lx-1,ly-1);
	}
#undef lowbit
};
template<typename T=ll>class segtree{
	fenwick<T>t0,t1,t2,t3;
	T pre(int x,int y){
		return	x*y*t0.query(x,y)
			-	x*  t1.query(x,y)
			-	  y*t2.query(x,y)
			+		t3.query(x,y);
	};
public:
	segtree(int n,int m):t0(n+5,m+5),t1(n+5,m+5),t2(n+5,m+5),t3(n+5,m+5){}
	void add(int lx,int ly,int rx,int ry,T cnt){
		t0.add(lx,ly,cnt);t0.add(lx,ry+1,-cnt);t0.add(rx+1,ly,-cnt);t0.add(rx+1,ry+1,cnt);
		t1.add(lx,ly,cnt*ly);t1.add(lx,ry+1,-cnt*(ry+1));t1.add(rx+1,ly,-cnt*ly);t1.add(rx+1,ry+1,cnt*(ry+1));
		t2.add(lx,ly,cnt*lx);t2.add(lx,ry+1,-cnt*lx);t2.add(rx+1,ly,-cnt*(rx+1));t2.add(rx+1,ry+1,cnt*(rx+1));
		t3.add(lx,ly,cnt*lx*ly);t3.add(lx,ry+1,-cnt*lx*(ry+1));t3.add(rx+1,ly,-cnt*(rx+1)*ly);t3.add(rx+1,ry+1,cnt*(rx+1)*(ry+1));
	};
	T query(int lx,int ly,int rx,int ry){
		++lx,++ly,++rx,++ry;
		return pre(rx,ry)-pre(rx,ly-1)-pre(lx-1,ry)+pre(lx-1,ly-1);
	};
};
ll n,q,m;
map<array<int,4>,unsigned long long>mp;
mt19937_64 mt(chrono::system_clock::now().time_since_epoch().count());// 随机数
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>m>>q;
	segtree<unsigned long long>sgt(n,m);
	while(q--){
		int op;
		array<int,4>arr;
		cin>>op>>arr[0]>>arr[1]>>arr[2]>>arr[3];
		if(op==1){
			mp[arr]=mt();// 存下来
			// cout<<sgt.query(4,4,4,4)<<"\n";// debugging
			sgt.add(arr[0],arr[1],arr[2],arr[3],mp[arr]);
			// cout<<sgt.query(4,4,4,4)<<"\n";
		}else if(op==2){
			// cout<<sgt.query(4,4,4,4)<<"\n";
			sgt.add(arr[0],arr[1],arr[2],arr[3],-mp[arr]);
			// cout<<sgt.query(4,4,4,4)<<"\n";
		}else{
			cout<<(sgt.query(arr[0],arr[1],arr[0],arr[1])==sgt.query(arr[2],arr[3],arr[2],arr[3])?"Yes":"No")<<"\n";
		}
	}
	return 0;
}

G

直接看我题解去吧,不想再写一遍了

TLE \(3\) 发原因:使用 \(16\) 常大数的区间修区间查。

最后来一个大大的警示后人

posted on 2025-12-23 17:17  HydrogenOxygen  阅读(13)  评论(0)    收藏  举报