二维树状数组
二维树状数组
二维较一维只多了一层循环。
对于每个位置 \((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}\)。
则
又
其中 \(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}\) 不被求和。
所以优化了两层循环。
好的。
一维怎么写的?
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\) 常大数的区间修区间查。
最后来一个大大的警示后人。
浙公网安备 33010602011771号